本文由CocoaChina翻譯組成員YueWang翻譯自raywenderlich,敬請指正。
原文:WatchKit FAQ,作者:Soheil Azarpour
Ray友情提示:這篇關於WatchKit的文章是作為 Spring Swift Fling慶賀的一部分而發表。希望你們會喜歡它!
在寫這篇文章時,開發者接觸WatchKit差不多有3個月時間了。正因為開發者在慢慢地了解這個新的酷炫的技術,一大堆問題毫無疑問地隨之而來。
在這篇WatchKit FAQ裡,我們將會解答一系列我們在社區、Twitter、郵件以及Stack Overflow上問的比較頻繁的問題。 我們也將會在新的問題不斷湧現時,周期性地更新這個FAQ。
對於一些沒有明確解決方案的問題,答案就綜合了開發者的智慧、觀點以及部分合理推測等。跟你一樣,我們也是不斷的探索中不斷的學習WatchKit,而且這項技術也正在不斷的開發當中,所以也在不斷地變化。
關於WatchKit中喜歡的和不喜歡的地方,歡迎和我們分享你的觀點和評論,然後提出更多的問題。我們將會根據你的回饋更新這個FAQ。
基礎問題
什麼是WatchKit,以及它如何工作的?
WatchKit是蘋果公司為Apple Watch創建混合型應用程序的框架,且是和Xcode 6.2綁在一起的。
WatchKit工作原理是將你的APP拆分成兩個不同的部分:
Apple Watch只包含storyboard和asset catalog 之類的界面資源,即便它可以處理用戶輸入,但它實際上根本不會執行任何代碼。換句話說, Apple Watch更像是一個精簡的客戶端。
iPhone包含了所有的代碼,而且把它作為一個擴展來執行,就跟Today或Action擴展一樣。
一個很酷的事情是Apple Watch和iPhone的通信是自動的,而且這一切只發生在後台。
你按照之前的習慣工作,而WatchKit將代你處理所有的無線通信。就你寫的代碼而言,你的views和outlets是本地關聯的,盡管它們可能是在完全分開的設備上。非常酷!
想學更多,查看我們的WatchKit: Initial Impressions文章。
可以用Swift 編寫Apple Watch 應用嗎?
是的,你可以用Objective-C或者Swift來創建Apple Watch應用,或者兩者都用。蘋果已經為WatchKit提供了兩個模板工程:
WatchKit Catalog: Using WatchKit Interface Elements--是全部用Objective-C寫的。
Lister: A Productivity App--兩種語言編寫:Objective-C和Swift.
除此之外,我們的 WatchKit Tutorial, WatchKit video tutorial series和 WatchKit by Tutorials書都是專門用Swift寫的。
蘋果也已經提供了用Objective-C和Swift寫的 WatchKit Framework的文檔。
我可以自定義的表盤嗎?
不行。目前還不支持自定義表盤。
一個iPhone可以關聯幾個Apple Watch?
一個iPhone每次可關聯一個Apple Watch--它們是一對一的關系。
我可以用我的iPad關聯我的Apple Watch嗎?
不可以。目前一個Apple Watch只能跟iPhone配對。
iPhone應用可以喚醒它的WatchKit擴展和watch應用嗎?
不能。WatchKit 擴展只能讓系統去啟動關聯iPhone應用,這一切都在後台運行。目前還不支持反向操作。
第三方應用可以從Watch應用打電話嗎?
不可以。沒有任何公有API可以讓你直接從WatchKit extension打電話。因為它關聯的iPhone應用也並不會被帶到前台,系統會默默忽略所有來自關聯iPhone應用程序的撥打電話或者openURL:請求。
可以從watch應用接入手表上的心跳傳感器和其他傳感器嗎?
不可以。目前沒有任何API可以接入到Apple Watch上的硬件傳感器。
Short-Look和Long-Look,靜態和動態通知之間有什麼區別?
Short-Look通知:Short-Look 通知是由系統提供的。類似於你在iPhone上收到的通知banner,對於Short-Look通知,你沒有任何控制權。Short-Look 通知可展示應用程序的icon、名稱以及標題。當通知進來時,用戶就會看見這個Short-Look通知。如果用戶舉起手腕,經過一個短暫停留,Short-Look通知就會轉變到Long-Look通知。
Long-Look通知: Long-Look通知可以是靜態或動態的。
Static通知: 靜態通知包含一個利用通知payload自動填充了的單一標簽。你可以在watch app的storyboard裡面創建一個靜態通知場景,但是除了改變sash和標題的顏色外,你不能真正地對其進行自定義。
Dynamic通知: 動態通知要求你子類化InterfaceController。它從storyboard實例化,而且你可以提供自己的定制界面。注意一點,並不能擔保動態通知界面肯定會展現出來。例如,如果手表的電池量很低時,系統可能會決定展現靜態通知界面以便節省電量,因為創建靜態通知界面成本更低。
進階問題
選擇合適的scheme
我怎樣能使用模擬器來測試glance或者通知?
每個glance或通知需要使用自己專用的build scheme才能在模擬器中運行。然後你簡單地選擇合適的scheme,然後構建並運行 。
可以疊放界面元素嗎?
不可以。本來界面元素是不能疊放在一起的。不過我們有變通方法。比如,你可以使用WKInterfaceGroup,並將其背景圖片設置為展示你想疊加的控件的背景。在這個分組裡,你可以增加必要的標題、按鈕以及其他等等。
可以定制界面元素的CALayer屬性嗎?
不可以。現在Apple Watch界面元素還沒有可用的CALayer屬性,因為它們不是繼承自UIView或CALayer。
WatchKit中可用的類能進行子類化嗎?
沒有什麼可以阻止你子類化WatchKit裡的類,但是你可能會發現它不能使用。你可以子類化一些類,比如WKInterfaceController和WKUserNotificationInterfaceController InterfaceController,並在storyboard裡面使用它們。
不過,watch app的storyboard不允許你改變任何界面元素的類。而且你已經不能動態地創建界面元素,然後像子視圖一樣插入或者刪除它們。你只能隱藏或者顯示已經展現在storyboard中的界面元素。
可以混用基於分頁的和基於導航的界面控制器?
可以,但有一些限制。如果應用有一個hierarchical interface(分層界面),你可以展示模態地展示一個基於分頁(page-based)的界面。如果任何頁面都是hierarchical interface,那會展示界面的根界面控制器,並且你不能向其堆棧上推送任何東西。如果應用包含一個page-based界面,那你可以模態地展示一個hierarchical interface,但只展示根界面控制器,並且你也不能向其堆棧上推送任何東西。
Apple Watch上有 UIActivityIndicator或UIAlertController的等價對象嗎?
沒有,但是你可以通過模態地展示一個自定義WKInterfaceController來代替UIAlertController。
你可以通過增加一系列圖片創建所需動畫,從而替代一個活動指示器,或者僅僅展示一個包含合適文本的標簽。舉個例子,查看蘋果的Lister example. 在Watch App的glance裡,你會看到360張圖片,可用以展示單一的圓圈動畫。
我可以使用Core Graphics來動態地創建圖片,並添加到watch app中嗎? 它們可以緩存在Apple Watch上嗎?
可以。但是任何使用Core Graphics的圖片組合必須作為擴展的一部分在iPhone上進行。一旦你將Core Grahphics的繪畫上下文傳遞給了一個UIImage的實例,你就可以在手表上用WKInterfaceDevice的addCachedImage(_:name:)方法來緩存它。
我可以在Apple Watch上使用定制視圖嗎? 我可以自定義公有API之外的界面元素嗎?
不可以,你不可以使用定制視圖。WatchKit僅僅支持一些原生的界面元素。除了他們的公有API之外,任何界面元素都不能被繼承或者自定義。可用的界面元素有WKInterfaceLabel、WKInterfaceButton、WKInterfaceImage、WKInterfaceGroup、WKInterfaceSeparator、WKInterfaceTable、WKInterfaceSwitch、WKInterfaceMap、WKInterfaceSlider以及WKInterfaceTimer.
Apple Watch是怎麼樣跟你的iPhone交互的?
Apple Watch利用Bluetooth LE 和 Wi-Fi 技術來同它匹配的iPhone進行交互。交互和實現的本質方法對於使用者和開發者來說都是不透明的。
進入飛行模式後打開Wi-Fi 和 Bluetooth
飛行模式狀態下可以使用Apple Watch嗎?
是的。實際上,在打開飛行模式後你可以打開Bluetooth和Wi-Fi來實現交互,並繼續使用你的Apple Watch。
當Apple Watch不能同它匹配的iPhone通信時,我的app將會怎樣?
簡單的說,你的APP不會運行,而且如果它正在運行的話也將會掛起。
在WatchKit擴展中,didDeactivate()會在當前的interface controller下被調用,你將有機會在這裡做任何必要的清理工作。一個紅色的iPhone圖標會展示在手表上的狀態欄以指示連接丟失,而且這個界面將會停留在屏幕上,但並不會交互。用戶可以要麼修復連接要麼退出app。
Watch app怎麼同它匹配的iPhone app 通信?
這裡有許多的技術你可能會用到,一個流行的技術就是你的watch app在一個共享的容器裡寫或更新數據,並且它負責通知iPhone app。之後,iPhone app可以從這個共享的容器中拉取變化。
另一個技術是通過一個字典傳送數據給iPhone app,但是這只能由watch app發起。在WatchKit extension中有一個單一的API;調用WKInterfaceController類的類方法openParentApplication(userInfo:reply:),正如下面的代碼塊所展示的那樣:
// Notify companion iPhone app of some changes in the shared container. let kSharedContainerDidUpdate = "com.rayWenderlich.shared-container.didUpdate" let requestInfo: [NSObject: AnyObject] = [kSharedContainerDidUpdate: true] WKInterfaceController.openParentApplication(requestInfo) { (replyInfo: [NSObject : AnyObject]!, error: NSError!) -> Void in // Handle the reply from the companion iPhone app... }
在userInfo 字典裡,你簡單地傳一個flag或一些數據給匹配的iPhone app使用。為了接收這個通信,匹配的iPhone app必須在其app delegate裡實現application(_:handleWatchKitExtensionRequest:reply:)方法。
func application(application: UIApplication!, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!, reply: (([NSObject : AnyObject]!) -> Void)!) { let kSharedContainerDidUpdate = "com.rayWenderlich.shared-container.didUpdate" if let isUpdate = userInfo[kSharedContainerDidUpdate] as? Bool { // Process request, then call reply block reply(...) } }
如果匹配的iPhone app被掛起或者被終止了,系統會在後台拉起它。根據交流的目的性,匹配的iPhone app可能會在回復block裡返回信息以便watch app可以相應的做出處理。
iPhone app如何同它的watch app通信?
沒有任何辦法可以讓一個iPhone app向它的擴展發起通信。除開在一個共享的容器中寫數據,或者回應watch app的請求,iPhone app可以使用Darwin Notification Center通知WatchKit extension一個特定的事件- Darwin Notification Center是Core Foundation框架的一個API。
如果你決定使用Darwin Notification Center,有一些非常重要的事情需要牢記:
一個應用只有唯一一個Darwin Notification Center。
所有的Darwin通知都是系統級的。
為了能將通知發出, 主線程的run loop必須在一個常用的模式下運行,比如kCFRunLoopDefaultMode。
watch app和iPhone app都必須在前台運行,才能處理發送和接收Darwin 通知。
你不能通過Darwin 通知傳送對象,因為他們只能攜帶一個名字和一個userInfo字典。
Darwin通知是非持久性的,相反是即時傳送的。因此,如果一個觀察者被掛起、終止或者放置在後台中,通知就會丟失。
如果我沒有實現靜態或動態通知界面會怎樣?
即時你沒有watch app,系統仍會為你的iPhone app在手表上展示通知。 但是,默認的通知界面沒有定制化風格。如果你有交互性的通知,系統會在手表上隱藏這些操作。
setImage(_:) 和setImageNamed(_:)有什麼不同?
如果你想展示的圖片在手表上被緩存了或在watch app bundle裡的asset catalog裡,你應該使用setImageNamed(_:)方法。如果圖片沒有被緩存,可使用setImage(_:)方法,它將通過無線方式將圖片數據傳送到Apple Watch。
當滿足以下條件之一時,圖片就在手表上進行了緩存:
1.在工程中同watch app target捆綁在一起,意味著在工程中圖片屬於Watch App target的asset catalog。
2.事先通過 WKInterfaceDevice APIs:addCachedImage(_:name:) 或addCachedImageWithData(_:name:)其中之一在手表上明確地進行了緩存。
可以在watch App上使用iCloud嗎?
是的。你可以在Apple Watch app中使用iCloud。Lister: A Productivity App是蘋果提供的示例工程,演示了怎樣在上下文中使用iCloud。
動畫
我怎樣在watch app中添加動畫?
只有唯一一個方法可以在Apple Watch上展示動畫: image sequences。 想要讓一些內容看起來是動畫的,你必須事先做好一系列的圖片,然後像flip-book那樣重覆循環他們。動畫GIF的時代又回來了!
你可以在WKInterfaceImage對象裡通過展示一系列靜態圖片來創建自定義動畫。
@IBOutlet weak var image: WKInterfaceImage? ... image?.setImageNamed(image1) // Load the initial image using the required format image?.startAnimating() // Starts animating ... image?.stopAnimating() // Optional. Stops animating.
你也可以使用下面的代碼片段讓一部分的圖片動畫。
image?.startAnimatingWithImagesInRange(range, duration: 2, repeatCount: 1)
我可以通過編碼為Apple Watch創建動畫嗎?
可以,但是可能不會是以你想的那樣實現-就像我前面說的--目前沒有Core Animation框架或類似框架。你可以使用Core Graphics將動畫的每一幀渲染為離屏上下文,將其渲染為UIImage的實例,最後在Apple Watch上你將有一系列的可動畫圖片。
下面的代碼片段展示了如何使用Core Graphics通過為每幀生成一個圖像序列來創建一個移動的圓圈動畫。(代碼來自Jack Wu的WatchKit by Tutorials)
// Create an offscreen context UIGraphicsBeginImageContextWithOptions(size, opaque, scale) let context = UIGraphicsGetCurrentContext() for centerX in 0..100 { // Draw a circle at centerX. // Create a snapshot. let image = UIGraphicsGetImageFromCurrentImageContext() // Write the image as a file let data = UIImagePNGRepresentation(image) let file = "path\\image_\(centerX).png" data.writeToFile(file, atomically: true) } // End the context. UIGraphicsEndImageContext()
在此查看動圖
Apple Watch上的最大動畫幀率是多少?
你不能在Apple Watch上設置動畫幀率。但是,設置動畫長度是可以的,讓系統自動決定幀率。
如果圖像系列是無線傳輸的,比如當圖片沒被緩存時,幀率可達10fps。如果圖片已經在Apple Watch上通過圖片緩存或者在WatchKit app bundle裡的asset catalog 裡緩存了,幀率可達30fps。
調試和單元測試
我如何用模擬器同時運行和調試iPhone app 和Apple Watch App?
構建並運行watch app;啟動Apple Watch模擬器,運行watch app,並且運行調試器。
然後在iOS模擬器上,點擊iPhone app 的圖標運行它。
回到Xcode, 在頂部菜單選擇Debug\Attach To Process ,然後選擇相應的iPhone app。Xcode Debug Navigator裡會增加一個新的進程,並連接iPhone app和調試器。
WatchKit擴展如何進行單元測試?
你可以用你為iPhone或iPad 應用寫單元測試的方法來寫你的watch應用單元測試;簡單地為工程的WatchKit extension加一個新的單元測試target。但是,你不能將watch app指定為Host Application。
對於iPhone app target, iPhone應用展示為Host Application。
但是對於WatchKit Extension target而言是沒有Host Application的。
你必須明確地為WatchKit extension單元測試target添加每個你想要測試的文件。
從WatchKit擴展裡添加你想要測試的文件到單元測試的target。
Sharing Data(共享數據)
怎樣在WatchKit擴展和它的containing iOS app間共享數據?
你需要使用App Groups; 它引用了擴展和containing iPhone app都可以訪問的本地文件系統中的一個容器。你可以定義多個App Groups,然後不同的擴展都可以使用。
一旦App Groups啟用,你可以根據需要使用以下任何一種技術:
直接的在一個共享的容器中讀和寫。通過調用NSFileManager來獲得共享容器的URL,所用API為:
let kMyAppGroupName = "com.raywenderlich.mywatchapp.container" var sharedContainerURL: NSURL? = NSFileManager.defaultManager(). containerURLForSecurityApplicationGroupIdentifier(kMyAppGroupName)
使用共享的NSUserDefaults。想要在共享容器中創建默認的存儲,你需要該用NSUserDefaults(suiteName:)方法初始化一個新的NSUserDefaults 實例,並且傳遞App Groups的唯一identifier,如下:
let kMyAppGroupName = "com.raywenderlich.mywatchapp.container" let sharedUserDefaults = NSUserDefaults(suiteName: kMyAppGroupName)
注意:當你想向一個共享的容器中讀或寫時,你必須做的非常協調以防止數據損壞,因為一個共享容器可以被不同的進程同時訪問。推薦使用NSFilePresenter和NSFileCoordinator協調讀和寫。但是,我們還建議不要在app擴展中使用文件協調API,因為它們會導致死鎖。
引起問題的原因是app擴展的生命周期。App extensions 只有3個狀態:(a) running(運行);(b) suspended(掛起)以及(c) terminated(終止)。如果一個使用了文件協調API的app擴展在寫入時被掛起了,它永遠不會有機會讓出所有權,因此其他進程就死鎖了。
但是,一個iPhone或者iPad應用在後台時通過 app delegate 被通知,這時可以移除file presenters。當應用被帶回到前台時,file presenters可以被重新添加。
替代方法是,你可以使用atomic safe-save 操作,比如NSData‘s writeToURL(_:atomically:)方法。SQLite和 Core Data也可以允許多個進程安全的共享同一個容器裡的數據,即便其中一個在事務處理當中時被掛起。
你可以在Technical Note TN2408: Accessing Shared Data from an App Extension and its Containing App中學到更多。
怎樣在Watch app和iPhone app中共享一個Core Data數據庫?
想要共享一個Core Data 永久存儲文件,你本質上使用跟共享數據示例一樣的方法。下面的代碼段展示了怎樣實現該任務:
let kMyAppGroupName = "com.raywenderlich.mywatchapp.container" var sharedContainerURL: NSURL? = NSFileManager.defaultManager(). containerURLForSecurityApplicationGroupIdentifier(kMyAppGroupName) if let sharedContainerURL = sharedContainerURL { let storeURL = sharedContainerURL.URLByAppendingPathComponent("MyCoreData.sqlite") var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) coordinator?.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: nil) }
聊聊商業化的問題
我可以為Apple Watch 創建游戲嗎?什麼類型的游戲適合?
現在說什麼樣類型的游戲會成功和用戶是否願意在手表上玩游戲還太早。毫無疑問,你需要從不同角度來思考Apple Watch。還記得iPhone和iPad上的游戲同筆記本上的游戲相比時用戶心理的不同嗎?同樣地,Apple Watch 要求一個獨一無二的途徑。
我們已經知道能做的很有限,因為還沒有能在Apple Watch上訪問硬件的API,或者支持手勢識別,或者允許用戶屏幕上自定義繪制。記住,你僅僅可以使用原始的界面元素。
但是不要讓這些限制阻礙了你的想象力,要把它們當做是基本規則。
怎樣通過Apple Watch apps 賺錢?
現在說這個有點言之過早。注意一件事:它不支持iAd,基於小屏幕尺寸和用戶與Watch app交互時間有限來考慮,屏幕上的廣告可能會使用戶厭煩,以及不論以何種方式表現在收益上都不是那麼值得。
同樣,如果WatchKit 擴展被包含在app bundle裡,你不能禁用它或者說阻止用戶安裝它,所以它不能作為通過IAP解鎖某些功能的補充。
但是,仍有幾條WatchKit extension貨幣化的方法:
如果你在App Store中有一個免費版本應用和一個付費版本應用,你可以僅僅在付費版本的應用中實現WatchKit extension。
如果你的一個應用使用了IAP,你可以在擴展中僅僅展示有限的信息,但是允許用戶通過IAP解鎖額外的功能。
這顯然不是一個所有可能貨幣化WatchKit app的詳盡方法列表, 但是有一點很清楚:你將會盡可能的發揮創造力來提高使app貨幣化,就像你開發watch apps本身一樣。
有沒有什麼理由可以讓開發者相信,僅僅為App Store開發watch apps 也能營生?
盡管Apple Watch確實是一個充滿了機會的全新平台,但它不太可能創造一個類似App Store最初亮相時的黃金時代。
注意:Apple Watch是一個完全不同的命題。基於它的美學價值和價格標簽,它更類似於一個可以同iPhone溝通的視頻,而不是基本的設備。
不過,WatchKit擴展可以更容易讓app脫穎而出。像當初原生iPad 應用比僅僅按比例伸縮匹配的iPhone應用更成功一樣。一款深思熟慮的,設計優良的匹配iPhone app 的Apple Watch app可以促進銷售。
更多問題?
如果你還有是這裡沒提及到的問題(我知道你有!)可發布評論告知。我們會選出頻率最高的,吸引人的,甚至是有挑戰性的問題。
http://www.raywenderlich.com/94672/watchkit-faq