# **Chapter 6—Accessing Native APIs**
NativeScript之美,就是你可以用javascript,xml,和css寫出原生ios和安卓app,而不用觸及 Swift, Objective-C, 或者 Java,如果你選擇的話。但是如果你想給用戶奉獻出一款不同的,更具平臺適應性的用戶界面呢?或是如果你想訪問 NativeScript 沒有通過 NativeScript 模塊和插件暴露的iOS 或 Android API 呢?
NativeScript如愿地給予了你深入原生代碼的選擇,并且實現它而不用離開 JavaScript 。要看這個工作是如何運轉的,我們從給IOS版本的雜貨店App的 ActionBar 添加一些顏色開始。
## **目錄**
* 6.1: 自定義 ActionBar - iOS
* 6.2: 從列表中刪除 - Android
* 6.3: 從列表中刪除 - iOS
## **[6.1: 自定義ActionBar - iOS](http://docs.nativescript.org/tutorial/chapter-6#61-customize-the-actionbar---ios)**
當你使用 ActionBar UI 組件的時候,, NativeScript 實際上在為你創建和管理一個 iOS`UINavigationController` 。你可以通過深入實現的代碼自己了解這點,特別是`node_modules/tns-core-modules/ui/action-bar/action-bar.ios.js`和`node_modules/tns-core-modules/ui/frame/frame.ios.js`文件。
你可能意識不到的是,你在這些 NativeScript 模塊里看到的代碼,這些看起來有些 Objective-C化或者 Java化的JavaScript代碼,是可以讓你用在你自己的 JavaScript 模塊里的。例如,你可以輕易地得到 `UINavigationBar` 的一個引用然后簡單地調用它的 [documented 方法](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UINavigationBar_Class/index.html) 來改變其外觀和感覺。我們來看如何做到。
> ### **操作: 修改 ActionBar**
>
> ---
>
> 在你開始搗鼓 `UINavigationBar` 之前,我們添加一個 `<ActionBar>` 到登錄和注冊頁面,那么app的三個頁面都有統一的外觀了。
>
> 打開 `app/views/register/register.xml` 并 緊接著開標簽 `<Page>` 粘貼下面的代碼:
>
> `<Page.actionBar>`
>
> `<ActionBar title="Sign up">`
>
> `</ActionBar>`
>
> `</Page.actionBar>`
>
> 然后,打開 `app/views/login/login.xml` 并粘貼下面的代碼, 同樣緊接著開標簽 `<Page>` ;
>
> `<Page.actionBar>`
>
> `<ActionBar title="Sign in"></ActionBar>`
>
> `</Page.actionBar>`
>
> 最后, 打開 `app/app.css` 粘貼下面的CSS 以修改 ActionBar的顏色;
>
> `ActionBar {`
>
> `color: white;`
>
> `background-color: #2E6DAD;`
>
> `}`
>
> ActionBar就位了,我們來看如何用一些原生IOS API來自定義它的外觀。打開 `p/views/login/login.js` 并在 `exports.loaded()` 函數里面粘貼下面的代碼,緊接著 `ar page = args.object;` 語句:
>
> `if (page.ios) {`
>
> `var navigationBar = frameModule.topmost().ios.controller.navigationBar;`
>
> `navigationBar.barStyle = UIBarStyle.UIBarStyleBlack;`
>
> `}`
好了,我們看下剛才發生的事情,從 `if (page.ios)` 檢查開始。通常,NativeScript 模塊遵循的模式是通過IOS和android屬性來暴露它們的原生實現。你可以在 if 檢查 \(`page.ios`\) 里看到這點,并且在if檢查的第一行, `meModule.topMost().ios` 被用來檢索一個底層 `UINavigationController` 的引用。對現有的這些屬性 \(比如`if (page.ios)`\) 的測試是一個方便的方法來分支你的代碼,以確信IOS特定代碼只運行在IOS上,而 Android 的特定代碼只運行在 Android 上。
> ### 提示:
>
> ---
>
> 最好的操作是,當你有一些不多的特定平臺的改變要實施的時候,用一個 **if **檢查來測試平臺是可行的路徑。相反地,如果你有大量的,完全不同的代碼塊針對IOS和android的話,你可能希望采取 [特定平臺的后臺代碼文件](http://docs.nativescript.org/tutorial/chapter-6#platform-specific-files) ——比如: `login.ios.js` 和 `login.android.js` 。
在`if`塊里面,你從獲取一個 `UINavigationBar` 的引用開始,然后設置它的 `barStyle`[ property](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIKitDataTypesReference/index.html#//apple_ref/c/tdef/UIBarStyle) 到 `UIBarStyle.UIBarStyleBlack` ,這\(反直覺地?\)讓IOS狀態欄使用白色文本。它產生的外觀顯示如下:

學習如何將IOS和ANDROID API 轉換為 NativeScript 代碼會有個嘗試和試錯的過程,直到掌握。你可以隨時參考 NativeScript 文檔里詳細描述是如何處理代碼轉換的。這里是 [Android的文檔](http://docs.nativescript.org/runtimes/android/marshalling/java-to-js.html),這里是[iOS的文檔](http://docs.nativescript.org/runtimes/ios/marshalling/Marshalling-Overview.html)。
> ### 提示:
>
> ---
>
> NativeScript為所有 IOS和ANDROID API提供了 TypeScript 聲明文件。如果你使用 TypeScript 就可以引用這些聲明文件以在你的編輯器里以使編碼實現。即便你沒有使用 TypeScript ,當你要轉換原生API為 NativeScript 代碼時,這些聲明文件也可以是寶貴的參考資料。例如要視圖在IOS聲明文件里搜索 “UINavigationBar” 看那些其它的屬性可用。
>
> * [iOS TypeScript 聲明文件](https://github.com/NativeScript/NativeScript/tree/master/tns-platform-declarations/ios/objc-i386)
> * [Android TypeScript 聲明文件](https://raw.githubusercontent.com/NativeScript/NativeScript/master/tns-platform-declarations/android/android17.d.ts)
分叉用戶體驗帶來的不只是簡單地改變一些顏色。比如,華東刪除列表項目是一個常用的IOS互動行為,而Andriod木有。但正如你看到的, NativeScript 使其比較容易地分叉你的代碼以提供一個更加適配平臺的體驗。那么要允許用戶從列表中刪除一個項目,我們就先為IOS創建一個滑動刪除UI,然后使用一個更**安卓范**的垃圾筐圖標來讓用戶從Android App刪除項目。
## [**6.2: 從列表刪除 - Android**](http://docs.nativescript.org/tutorial/chapter-6#62-deleting-from-a-list---android)
對于安卓,你準備添加一個可點擊的垃圾筐到雜貨列表的每一項;那么首要的挑戰就是指出如何只是在安卓里面顯示這些圖片,而在IOS上使用一個完全不同的UI。
為此你將在XML使用文件里使用一些新的語法。 NativeScript 允許你使用 `platform:attributeName` 語法來為單獨的平臺設置屬性。例如下面的代碼將按鈕的文本在IOS上設置為“foo”,而在Android上設置為“bar”。
> `<Button ios:text="foo" android:text="bar" />`
同樣的語法對于所有UI組件的所有屬性都有效,并且這個機制是 NativeScript 允許你叉分你的代碼來區分iOS和Android實現的一個可行的路徑。
> ### **操作: 添加一個針對安卓的UI元素**
>
> ---
>
> 打開 `app/views/list/list.xml,` 找到 `<ListView.itemTemplate>` 標簽, 用下面的代碼替換:
>
> `<ListView.itemTemplate>`
>
> `<GridLayout columns="*, auto">`
>
> `<Label text="{{ name }}"/>`
>
> `<Image src="res://ic_menu_delete" ios:visibility="collapsed" col="1" tap="delete" />`
>
> `</GridLayout>`
>
> `</ListView.itemTemplate>`
通過這段代碼你主要添加了一個 `<Image>` 到現有的 ListView 模板。但既然你在這個模板里有了復合UI組件,你就需要告訴 NativeScript 如何布局這兩個組件,這也就是 `<GridLayout>` 處理的問題。通過定義 `columns="*, auto"` 你把每個項目分成兩欄:第一欄包含 label ,第二欄包含新的圖片。
For the image itself, the `ios:visibility="collapsed"` attribute sets the image's `visibility` CSS property to `"collapsed"`, which hides it. Because the attribute was prefixed with `ios:`, that CSS property is only applied on iOS; therefore the button displays on Android devices, but not on iOS ones. The trash can image itself has already been placed in the app for you, and can be found in appropriate sizes in the four drawable folders in `/app/App_Resources/Android`. Here's what the trash can UI looks like on Android:
對于圖片本身, `ios:visibility="collapsed"` 屬性設置圖片的 `visibility` CSS屬性為 `collapsed"` ,把它隱藏起來。因為該屬性以 `ios:` 開頭,這個CSS屬性就只在IOS上生效;因此按鈕會顯示在Android上。垃圾筐圖片本身已經在app里為你放置好了,以合適的尺寸放置在在四個文件夾里,能在 `app/App_Resources/Android` 里找到。這里是垃圾筐UI在Android上看起來的樣子:

最后,讓垃圾筐實際地刪除項目。為此你需要在列表的后臺代碼文件實現 `tap="delete"` 處理器。
> ### **操作: 創建刪除函數**
>
> ---
>
> 打開 `app/views/list/list.js` 粘貼下面的代碼到文件底部:
>
> `exports.delete = function(args) {`
>
> `var item = args.view.bindingContext;`
>
> `var index = groceryList.indexOf(item);`
>
> `groceryList.delete(index);`
>
> `};`
>
> 這段代碼獲取了用戶點擊的雜貨的index,將其與視圖模型里相應的項目匹配,然后傳遞這個index帶視圖模型的 `delete()` 方法——這還不存在,所以我們來創建它。
>
> 打開 `app/shared/view-models/grocery-list-view-model.js` 并粘貼下面的代碼。記住直接添加這個函數到文件末尾,正好在 `return viewModel` 行上面:
>
> `viewModel.delete = function(index) {`
>
> `return fetch(config.apiUrl + "Groceries/" + viewModel.getItem(index).id, {`
>
> `method: "DELETE", headers: {`
>
> `"Authorization": "Bearer " + config.token,`
>
> `"Content-Type": "application/json"`
>
> `}`
>
> `})`
>
> `.then(handleErrors)`
>
> `.then(function() {`
>
> `viewModel.splice(index, 1);`
>
> `});`
>
> `};`
這段代碼現在看起來可能相當熟悉。你再次調用了 fetch 模塊的 `fetch()` 方法,這次是定義一個 `"DELETE"` 方法來從后臺刪除一個雜貨。你再次返回一個 `Promise` 所以回調函數能處理調用成功和失敗的調用。再次注意使用MVVM方式來搭建App的能力。為了更新雜貨列表UI,所有你要做的就是從 ObservableArray \(`viewModel.splice(index, 1)`\) 里刪除該項目,并讓列表的呈現自己處理變動。
If you run your app on Android you should be able to delete items from the list.
如果你在Android上運行app,你就可以從列表刪除項目了。

既然你為安卓的點擊圖標創建了接口,我們就來為IOS添加滑動刪除接口。
## [**6.3: 從列表刪除 - iOS**](http://docs.nativescript.org/tutorial/chapter-6#63-deleting-from-a-list---ios)
如果你是IOS用戶你可能相當熟悉互動刪除手勢,因為它普遍存在于很多IOS應用。實現該手勢的代碼實際上已經合并到 iOS SDK 本身了\(查看`UITableViewCellEditingStyle`[ 文檔](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewCell_Class/#//apple_ref/c/tdef/UITableViewCellEditingStyle) \),所以你可以直接在你的 NativeScript app里使用這些API。
實現這個的代碼比我們先前的Adroid示例要稍微麻煩些,所以我們在 `shared/utils/ios-swipe-delete.js` 文件準備了構建好的模塊來實現這個功能。在這個文件里你能找到一個自定義的支持 `UITableViewDataSource` 協議的數據源的實現。該文件輸出了一個單獨的 `eable()` 函數,它拿到一個 ListView 的引用,并自定義數據源行為注入到 ListView 。
不要對這些代碼的具體行為太過擔心,因為它涉及到一些IOS API怎樣工作的理解。最酷的是你可以用幾十行代碼就可以實現這個比較高級的IOS API實現,并且用一個非常簡單的 JavaScript API 就可以很容易地封裝這些代碼。這個使用上的簡單正是為什么 NativeScript 模塊和 NativeScript 插件那么容易運用。我們來看看如何使用這個模塊。
> ### **操作: 編輯 ListView**
>
> ---
>
> 添加下面的代碼到 `app/views/list/list.js`頂部:
>
> `var swipeDelete = require("../../shared/utils/ios-swipe-delete");`
>
> 然后,添加下面的代碼到 `exports.loaded()` 函數,直接在 `page = args.object;` 語句下面:
>
> `if (page.ios) {`
>
> `var listView = page.getViewById("groceryList");`
>
> `swipeDelete.enable(listView, function(index) {`
>
> `groceryList.delete(index);`
>
> `});`
>
> `}`
這節代碼拿到一個頁面的 `<ListView>` id的引用ing傳遞這個引用到滑動刪除模塊的 `enable()` 函數。這個 `enable()` 函數 同樣拿到一個回調,那么你另外通過一個內聯函數調用你在前節創建的視圖模型的 `delete()` 函數。這里是滑動刪除功能在IOS上看起來的樣子:

然后……就搞定了!您已經創建了一個功能性,跨平臺,后端驅動的app來管理您的雜貨列表。在這個進程中你為Android和IOS都創建了一個唯一的UI,利用了 leveraged 插件和npm模塊,學會了如何登錄和注冊,管理后臺服務,通過添加和刪除功能創建了列表,和更多……
恭喜你!盡情的 [在Twitter](https://twitter.com/intent/tweet?text=I%20just%20built%20an%20iOS%20and%20Android%20app%20using%20@NativeScript%20%F0%9F%8E%89.%20You%20can%20too!%20http://docs.nativescript.org/tutorial/chapter-0%20%23opensource) 或 [Facebook](https://www.facebook.com/sharer/sharer.php?u=http%3A%2F%2Fdocs.nativescript.org%2Ftutorial%2Fchapter-0&p%5B) 上 [share分享你的成就](https://twitter.com/intent/tweet?text=I%20just%20built%20an%20iOS%20and%20Android%20app%20using%20@NativeScript%20%F0%9F%8E%89.%20You%20can%20too!%20http://docs.nativescript.org/tutorial/chapter-0%20%23opensource) ,讓你的朋友們咋舌吧!
> ### **提示:**
>
> ---
>
> * 如果你對 NativeScript 如何實現直接調用IOS和Android API好奇,你可以在我們的博客閱讀 [“How NativeScript Works”](http://developer.telerik.com/featured/nativescript-works/) 。
> * 記住 [Groceries app's “end” branch](https://github.com/NativeScript/sample-Groceries/tree/end) 有這個教程的最終狀態。 你可以隨時回到這里參考。
> * 高級的 ListView 交互,比如滑動刪除, 拖動刷新, 以及其它的組件比如日歷和圖表都在盒子之外作為Telerik的 [UI For NativeScript](http://docs.telerik.com/devtools/nativescript-ui/introduction)產品實現了。(《=========也就是可能有收費!!!)