成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

iOS 通知中心擴展 制作總結(jié)與Demo

移動開發(fā) iOS
擴展 (Extension) 是 iOS 8 和 OSX 10.10 加入的一個非常大的功能點,開發(fā)者可以通過系統(tǒng)提供給我們的擴展接入點 (Extension point) 來為系統(tǒng)特定的服務提供某些附加的功能。

[[117425]]

本文是我的 WWDC 2014 筆記 中的一篇,涉及的 Session 有

Creating Extensions for iOS and OS X, Part 1

Creating Extensions for iOS and OS X, Part 2

總覽

擴展 (Extension) 是 iOS 8 和 OSX 10.10 加入的一個非常大的功能點,開發(fā)者可以通過系統(tǒng)提供給我們的擴展接入點 (Extension point) 來為系統(tǒng)特定的服務提供某些附加的功能。對于 iOS 來說,可以使用的擴展接入點有以下幾個:

1. Today 擴展 - 在下拉的通知中心的 "今天" 的面板中添加一個 widget

2. 分享擴展 - 點擊分享按鈕后將網(wǎng)站或者照片通過應用分享

3. 動作擴展 - 點擊 Action 按鈕后通過判斷上下文來將內(nèi)容發(fā)送到應用

4. 照片編輯擴展 - 在系統(tǒng)的照片應用中提供照片編輯的能力

5. 文檔提供擴展 - 提供和管理文件內(nèi)容

6. 自定義鍵盤 - 提供一個可以用在所有應用的替代系統(tǒng)鍵盤的自定義鍵盤或輸入法

系統(tǒng)為我們提供的接入點雖然還比較有限,但是不少已經(jīng)是在開發(fā)者和 iOS 的用戶中呼聲很高的了。而通過利用這些接入點提供相應的功能,也可以極大地豐富系統(tǒng)的功能和可用性。本文將先不失一般性地介紹一下各種擴展的共通特性,然 后再以一個實際的例子著重介紹下通知中心的 Today 擴展的開發(fā)方法,以期為 iOS 8 的擴展的學習提供一個平滑的入口。

Apple 指出,iOS 8 中開發(fā)者的中心并不應該發(fā)生改變,依然應該是圍繞 app。在 app 中提供優(yōu)秀交互和有用的功能,現(xiàn)在是,將來也會是 iOS 應用開發(fā)的核心任務。而擴展在 iOS 中是不能以單獨的形式存在的,也就是說我們不能直接在 AppStore 提供一個擴展的下載,擴展一定是隨著一個應用一起打包提供的。用戶在安裝了帶有擴展的應用后,將可以在通知中心的今日界面中,或者是系統(tǒng)的設置中來選擇開 啟還是關閉你的擴展。而對于開發(fā)者來說,提供擴展的方式是在 app 的項目中加入相應的擴展的 target。因為擴展一般來說是展現(xiàn)在系統(tǒng)級別的 UI 或者是其他應用中的,Apple 特別指出,擴展應該保持輕巧迅速,并且專注功能單一,在不打擾或者中斷用戶使用當前應用的前提下完成自己的功能點。因為用戶是可以自己選擇禁用擴展的,所 以如果你的擴展表現(xiàn)欠佳的話,很可能會遭到用戶棄用,甚至導致他們將你的 app 也一并卸載。

擴展的生命周期

擴展的生命周期和包含該擴展的你的容器 app (container app) 本身的生命周期是獨立的,準確地說。它們是兩個獨立的進程,默認情況下互相不應該知道對方的存在。擴展需要對宿主 app (host app,即調(diào)用該擴展的 app) 的請求做出相應,當然,通過進行配置和一些手段,我們可以在擴展中訪問和共享一些容器 app 的資源,這個我們稍后再說。

因為擴展其實是依賴于調(diào)用其的宿主 app 的,因此其生命周期也是由用戶在宿主 app 中的行為所決定的。一般來說,用戶在宿主 app 中觸發(fā)了該擴展后,擴展的生命周期就開始了:比如在分享選項中選擇了你的擴展,或者向通知中心中添加了你的 widget 等等。而所有的擴展都是由 ViewController 進行定義的,在用戶決定使用某個擴展時,其對應的 ViewController 就會被加載,因此你可以像在編寫傳統(tǒng) app 的 ViewController 那樣獲取到諸如 viewDidLoad 這樣的方法,并進行界面構(gòu)建及做相應的邏輯。擴展應該保持功能的單一專注,并且迅速處理任務,在執(zhí)行完成必要的任務,或者是在后臺預約完成任務后,一般需 要盡快通過回調(diào)將控制權(quán)交回給宿主 app,至此生命周期結(jié)束。

按照 Apple 的說法,擴展可以使用的內(nèi)存是遠遠低于 app 可以使用的內(nèi)存的。在內(nèi)存吃緊的時候,系統(tǒng)更傾向于優(yōu)先搞掉擴展,而不會是把宿主 app 殺死。因此在開發(fā)擴展的時候,也一定需要注意內(nèi)存占用的限制。另一點是比如像通知中心擴展,你的擴展可能會和其他開發(fā)人員的擴展共存,這樣如果擴展阻塞了 主線程的話,就會引起整個通知中心失去響應。這種情況下你的擴展和應用也就基本和用戶說再見了..

擴展和容器應用的交互

擴展和容器應用本身并不共享一個進程,但是作為擴展,其實是主體應用功能的延伸,肯定不可避免地需要使用到應用本身的邏輯甚至界面。在這種情況 下,我們可以使用 iOS 8 新引入的自制 framework 的方式來組織需要重用的代碼,這樣在鏈接 framework 后 app 和擴展就都能使用相同的代碼了。

另一個常見需求就是數(shù)據(jù)共享,即擴展和應用互相希望訪問對方的數(shù)據(jù)。這可以通過開啟 App Groups 和進行相應的配置來開啟在兩個進程間的數(shù)據(jù)共享。這包括了使用 NSUserDefaults 進行小數(shù)據(jù)的共享,或者使用 NSFileCoordinator 和 NSFilePresenter 甚至是 CoreData 和 SQLite 來進行更大的文件或者是更復雜的數(shù)據(jù)交互。

另外,一直以來的自定義的 url scheme 也是從擴展向應用反饋數(shù)據(jù)和交互的渠道之一。

這些常見的手段和策略在接下來的 demo 中都會用到。一張圖片能頂千言萬語,而一個 demo 能頂千張圖片。那么,我們開始吧。

Timer Demo

Demo 做的應用是一個簡單的計時器,即點擊開始按鈕后開始倒數(shù)計時,每秒根據(jù)剩余的時間來更新界面上的一個表示時間的 Label,然后在計時到 0 秒時彈出一個 alert,來告訴用戶時間到,當然用戶也可以使用 Stop 按鈕來提前打斷計時。其實這個 Demo 就是我的很早之前做的一個番茄工作法的 app 的原型。

為了大家方便跟隨這個 demo,我把初始的時候的代碼放到 GitHub 的 start-project 這個 tag 上了。語言當然是要用 Swift,界面因為不是 demo 的重點,所以就非常簡單能表明意思就好了。但是雖然簡單,卻也是利用了上一篇文章中所提到的 Size Classes 來完成的不同屏幕的布局,所以至少可以說在思想上是完備的 iOS 8 兼容了 =_=..

初始工程運行起來的界面大概是這樣的: 

初始工程

初始工程

簡單說整個項目只有一個 ViewController,點擊開始按鈕時我們通過設定希望的計時時間來創(chuàng)建一個 Timer 實例,然后調(diào)用它的 start 方法。這個方法接收兩個參數(shù),分別是每次剩余時間更新,以及計時結(jié)束(不論是計時時間到的完成還是計時被用戶打斷)時的回調(diào)方法。另外這個方法返回一個 tuple,用來表示是否開始成功以及可能的錯誤。

剩余時間更新的回調(diào)中刷新界面 UI,計時結(jié)束的回調(diào)里回收了 Timer 實例,并且顯示了一個 UIAlertController。用戶通過點擊 Stop 按鈕可以直接調(diào)用 stop 方法來打斷計時。直接簡單,沒什么其他的 trick。

我們現(xiàn)在計劃為這個 app 做一個 Today 擴展,來在通知中心中顯示并更新當前的剩余時間,并且在計時完成后顯示一個按鈕,點擊后可以回到 app 本體,并彈出一個完成的提示。

添加擴展 Target

第一步當然是為我們的 app 添加擴展。正如在總覽中所提到的,擴展是項目中的一個單獨的 target。在 Xcode 6 中, Apple 為我們準備了對應各類不同擴展點的 target 模板,這使得向 app 中添加擴展非常容易。對于我們現(xiàn)在想做的 Today 擴展,只需點選菜單的 File > New > Target...,然后選擇 iOS 中的 Application Extension 的 Today Extension 就行了。 

添加 target

添加 target

在彈出的菜單中將新的 target 命名為 SimpleTimerTodayExtenstion,并且讓 Xcode 自動生成新的 Scheme,以方便測試使用。我們的工程中現(xiàn)在會多出一個和新建的 target 同名的文件夾,里面主要包含了一個 .swift 的 ViewController 程序文件,一個叫做 MainInterface 的 storyboard 文件和 Info.plist。其中在 plist 里 的 NSExtension 中定義了這個 擴展的類型和入口,而配套的 ViewController 和 StoryBoard 就是我們的擴展的具體內(nèi)容和實現(xiàn)了。

我們的主題程序在編譯鏈接后會生成一個后綴為 .app 的包,里面包含主程序的二進制文件和各種資源。而擴展 target 將單獨生成一個后綴名為 .appex 的文件包。這個文件包將隨著主體程序被安裝,并由用戶選擇激活或者添加(對于 Today widget 的話在通知中心 Today 視圖中的編輯刪增,對于其他的擴展的話,使用系統(tǒng)的設置進行管理)。我們可以看到,現(xiàn)在項目的 Product 中已經(jīng)新增了一個擴展了。 

擴展的product

擴展的product

如果你有心已經(jīng)打開了 MainInterface 文件的話,可以注意到 Apple 已經(jīng)為我們準備了一個默認的 Hello World 的 label 了。我們這時候只要運行主程序,擴展就會一并安裝了。將 Scheme 設為 Simple Timer 的主程序,Cmd + R,然后點擊 Home 鍵將 app 切到后臺,拉下通知中心。這時候你應該能在 Toady 視圖中找到叫做 SimpleTimerTodayExtenstion 的項目,顯示了一個 Hello World 的標簽。如果沒有的話,可以點擊下面的編輯按鈕看看是不是沒有啟用,如果在編輯菜單中也沒有的話,恭喜你遇到了和 Session 視頻里的演講者同樣的 bug,你可能需要刪除應用,清理工程,然后再安裝試試看。一般來說卸載再安裝可以解決現(xiàn)在的 beta 版大部分的無法加載的問題,如果還是遇到問題的話,你還可以嘗試重啟設備(按照以往幾年的 SDK 的情況來看,beta 版里這很正常,正式版中應該就沒什么問題了)。

如果一切正常的話,你能看到的通知中心應該類似這樣: 

Hello World widget

Hello World widget

這種方式運行的擴展我們無法對其進行調(diào)試,因為我們的調(diào)試器并沒有 attach 到這個擴展的 target 上。有兩種方法讓我們調(diào)試擴展,一種是將 Scheme 設為之前 Xcode 為我們生成的 SimpleTimerTodayExtenstion,然后運行時選擇從 Today 視圖進行運行,如圖;另一種是在擴展運行時使用菜單中的 Debug > Attach to Process > By Process Identifier (PID) or name,然后輸入你的擴展的名字(在我們的 demo 中是 com.onevcat.SimpleTimer.SimpleTimerTodayExtension)來把調(diào)試器掛載到進程上去。 

調(diào)試擴展

調(diào)試擴展

#p#

在應用和擴展間共享數(shù)據(jù) - App Groups

擴展既然是個 ViewController,那各種連接 IBOutlet,使用 viewDidLoad 之類的生命周期方法來設置 UI 什么的自然不在話下。我們現(xiàn)在的第一個難點就是,如何獲取應用主體在退出時計時器的剩余時間。只要知道了還剩多久以及何時退出,我們就能在通知中心中顯示 出計時器正確的剩余時間了。

對 iOS 開發(fā)者來說,沙盒限制了我們在設備上隨意讀取和寫入。但是對于應用和其對應的擴展來說,Apple 在 iOS 8 中為我們提供了一種可能性,那就是 App Groups。App Groups 為同一個 vender 的應用或者擴展定義了一組域,在這個域中同一個 group 可以共享一些資源。對于我們的例子來說,我們只需要使用同一個 group 下的 NSUserDefaults 就能在主體應用不活躍時向其中存儲數(shù)據(jù),然后在擴展初始化時從同一處進行讀取就行了。

首先我們需要開啟 App Groups。得益于 Xcode 5 開始引入的 Capabilities,這變得非常簡單(至少不再需要去 developer portal 了)。選擇主 target SimpleTimer,打開它的 Capabilities 選項卡,找到 App Groups 并打開開關,然后添加一個你能記得的 group 名字,比如 group.simpleTimerSharedDefaults。接下來你還需要為 SimpleTimerTodayExtension 這個 target 進行同樣的配置,只不過不再需要新建 group,而是勾選剛才創(chuàng)建的 group 就行。 

啟用 App Groups

啟用 App Groups

然后讓我們開始寫代碼吧!首先是在主體程序的 ViewController.swift 中添加一個程序失去前臺的監(jiān)聽,在 viewDidLoad 中加入:

  1. NSNotificationCenter.defaultCenter()   
  2.     .addObserver(self, selector: "applicationWillResignActive",name: UIApplicationWillResignActiveNotification, object: nil) 

然后是所調(diào)用的 applicationWillResignActive 方法:

  1. @objc private func applicationWillResignActive() { 
  2.     if timer == nil { 
  3.         clearDefaults() 
  4.     } else { 
  5.         if timer.running { 
  6.             saveDefaults() 
  7.         } else { 
  8.             clearDefaults() 
  9.         } 
  10.     } 
  11.   
  12. private func saveDefaults() {   
  13.     let userDefault = NSUserDefaults(suiteName: "group.simpleTimerSharedDefaults"
  14.     userDefault.setInteger(Int(timer.leftTime), forKey: "com.onevcat.simpleTimer.lefttime"
  15.     userDefault.setInteger(Int(NSDate().timeIntervalSince1970), forKey: "com.onevcat.simpleTimer.quitdate"
  16.   
  17.     userDefault.synchronize() 
  18.   
  19. private func clearDefaults() {   
  20.     let userDefault = NSUserDefaults(suiteName: "group.simpleTimerSharedDefaults"
  21.     userDefault.removeObjectForKey("com.onevcat.simpleTimer.lefttime"
  22.     userDefault.removeObjectForKey("com.onevcat.simpleTimer.quitdate"
  23.   
  24.     userDefault.synchronize() 

這樣,在應用切到后臺時,如果正在計時,我們就將當前的剩余時間和退出時的日期存到了 NSUserDefaults 中。這里注意,可能一般我們在使用 NSUserDefaults 時更多地是使用 standardUserDefaults,但是這里我們需要這兩個數(shù)據(jù)能夠被擴展訪問到的話,我們必須使用在 App Groups 中定義的名字來使用 NSUserDefaults。

接下來,我們可以到擴展的 TodayViewController.swift 中去獲取這些數(shù)據(jù)了。在擴展 ViewController 的 viewDidLoad 中,添加以下代碼:

  1. let userDefaults = NSUserDefaults(suiteName: "group.simpleTimerSharedDefaults")   
  2. let leftTimeWhenQuit = userDefaults.integerForKey("com.onevcat.simpleTimer.lefttime")   
  3. let quitDate = userDefaults.integerForKey("com.onevcat.simpleTimer.quitdate"
  4.  
  5. let passedTimeFromQuit = NSDate().timeIntervalSinceDate(NSDate(timeIntervalSince1970: NSTimeInterval(quitDate))) 
  6.  
  7. let leftTime = leftTimeWhenQuit - Int(passedTimeFromQuit) 
  8.  
  9. lblTImer.text = "\(leftTime)"   

當然別忘了把 StoryBoard 的那個 label 拖出來:

  1. @IBOutlet weak var lblTImer: UILabel! 

再次運行程序,并開始一個計時,然后按 Home 鍵切到后臺,拉出通知中心,perfect,我們的擴展能夠和主程序進行數(shù)據(jù)交互了: 

讀取數(shù)據(jù)

讀取數(shù)據(jù)

在應用和擴展間共享代碼 - Framework

接下來的任務是在 Today 界面中進行計時,來刷新我們的界面。這部分代碼其實我們已經(jīng)寫過(當然..確切來說是我寫的,你可能只是看過),沒錯,就是應用中的 Timer.swift 文件。我們只需要在擴展的 ViewController 中用剩余時間創(chuàng)建一個 Timer 的實例,然后在更新的 callback 里設置 label 就好了嘛。但是問題是,這部分代碼是在應用中的,我們要如何在擴展中也能使用它呢?

一個最直接也是最簡單的想法自然是把 Timer.swift 加入到擴展 target 的編譯文件中去,這樣在擴展中自然也就可以使用了。但是 iOS 8 開始 Apple 為我們提供了一個更好的選擇,那就是做成 Framework。單個文件可能不會覺得有什么差別,但是隨著需要共用的文件數(shù)量和種類的增加,將單個文件逐一添加到不同 target 這種管理方法很快就會將事情弄成一團亂麻。你需要考慮每一個新加或者刪除的文件影響的范圍,以及它們分別需要適用何處,這簡直就是人間地獄。提供一個統(tǒng)一 漂亮的 framework 會是更多人希望的選擇(其實也差不多成為事實標準了)。使用 framework 進行模塊化的另一個好處是可以得益于良好的訪問控制,以保證你不會接觸到不應該使用的東西,然后,Swift 的 namespace 是基于模塊的,因此你也不再需要擔心命名沖突等等一攤子 objc 時代的煩心事兒。

現(xiàn)在讓我們把 Timer.swift 放到 framework 里吧。首先我們新建一個 framework 的 target。File > New > Target... 中選擇 Framework & Library,選中 Cocoa Touch Framework (配圖中的另外幾個選項可能在你的 Xcode 中是沒有的,請無視它們,這是歷史遺留問題),然后確定。按照 Apple 對 framework 的命名規(guī)范,也許 SimpleTimerKit 會是一個不錯的名字。 

建立框架

建立框架

接下來,我們將 Timer.swift 從應用中移動到 framework 中。很簡單,首先將其從應用的 target 中移除,然后加入到新建的 SimpleTimerKit 的 Compile Sources 中。

添加 framework 文件

添加 framework 文件

確認在應用中 link 了新的 framwork,并且在 ViewController.swift 中加上 import SimpleTimerKit 后試著編譯看看...好多錯誤,基本都是 ViewController 中說找不到 Timer 之類的。這是因為原來的實現(xiàn)是在同一個 module 中的,默認的 internal 的訪問層級就可以讓 ViewController 訪問到關于 Timer 和相應方法的信息。但是現(xiàn)在它們處于不同的 module 中,所以我們需要對 Timer.swift 的訪問權(quán)限進行一些修改,在需要外部訪問的地方加上 public 關鍵字。關于 Swift 中的訪問控制,可以參考 Apple 關于 Swift 的這篇官方博客,簡單說就是 private 只允許本文件訪問,不寫的話默認是 internal,允許統(tǒng)一 module 訪問,而要提供給別的 module 使用的話,需要聲明為 public。修改后的 Timer.swift 文件大概是這個樣子的。

修改合適的訪問權(quán)限后,接下來我們就可以將這個 framework 鏈接到擴展的 target 了。鏈接以后編譯什么的可以通過,但是會多一個警告: 

警告

警告

這是因為作為插件,需要遵守更嚴格的沙盒限制,所以有一些 API 是不能使用的。為了避免這個警告,我們需要在 framework 的 target 中聲明在我們使用擴展可用的 API。具體在 SimpleTimerKit 的 target 的 General 選項卡中,將 Deployment Info 中的 Allow app extension API only 勾選上就可以了。關于在擴展里不能使用的 API,都已經(jīng)被 Apple 標上了 NS_EXTENSION_UNAVAILABLE,在這里有一份簡單的列表可供參考,基本來說都是 runtime 的東西以及一些會讓用戶迷惑或很危險的操作(當然這個標記的方法很可能會不斷變動,最終一切請以 Apple 的文檔和實際代碼為準)。 

開啟 Extension Only

開啟 Extension Only

接下來,在擴展的 ViewController 中也鏈接 SimpleTimerKit 并加入 import SimpleTimerKit,我們就可以在擴展中使用 Timer 了。將剛才的直接設置 label 的代碼去掉,換成下面的:

  1. override func viewDidLoad() {   
  2.     //... 
  3.   
  4.     if (leftTime > 0) { 
  5.         timer = Timer(timeInteral: NSTimeInterval(leftTime)) 
  6.         timer.start(updateTick: { 
  7.                 [weak self] leftTick in self!.updateLabel() 
  8.             }, stopHandler: nil) 
  9.     } else { 
  10.         //Do nothing now 
  11.     } 
  12.   
  13. private func updateLabel() {   
  14.     lblTimer.text = timer.leftTimeString 

我們在擴展里也像在 app 內(nèi)一樣,創(chuàng)建 Timer,給定回調(diào),坐等界面刷新。運行看看,先進入應用,開始一個計時。然后退出,打開通知中心。通知中心中現(xiàn)在也開始計時了,而且確實是從剩余的時間開始的,一切都很完美: 

通知中心計時

通知中心計時

#p#

通過擴展啟動主體應用

最后一個任務是,我們想要在通知中心計時完畢后,在擴展上呈現(xiàn)一個 "完成啦" 的按鈕,并通過點擊這個按鈕能回到應用,并在應用內(nèi)彈出結(jié)束的 alert。

這其實最關鍵的在于我們要如何啟動主體容器應用,以及向其傳遞數(shù)據(jù)。可能很多同學會想到 URL Scheme,沒錯通過 URL Scheme 我們確實可以啟動特定應用并攜帶數(shù)據(jù)。但是一個問題是為了通過 URL 啟動應用,我們一般需要調(diào)用 UIApplication 的 openURL 方法。如果細心的剛才看了 NS_EXTENSION_UNAVAILABLE 的同學可能會發(fā)現(xiàn)這個方法是被禁用的(這也是很 make sense 的一件事情,因為說白了擴展通過 sharedApplication 拿到的其實是宿主應用,宿主應用表示憑什么要讓你拿到啊!)。為了完成同樣的操作,Apple 為擴展提供了一個 NSExtensionContext 類來與宿主應用進行交互。用戶在宿主應用中啟動擴展后,宿主應用提供一個上下文給擴展,里面最主要的是包含了 inputItems 這樣的待處理的數(shù)據(jù)。當然對我們現(xiàn)在的需求來說,我們只要用到它的 openURL(URL:,completionHandler:) 方法就好了。

另外,我們可能還需要調(diào)整一下擴展 widget 的尺寸,以讓我們有更多的空間顯示按鈕,這可以通過設定 preferredContentSize 來做到。在 TodayViewController.swift 中加入以下方法:

  1. private func showOpenAppButton() {   
  2.     lblTimer.text = "Finished" 
  3.     preferredContentSize = CGSizeMake(0, 100) 
  4.   
  5.     let button = UIButton(frame: CGRectMake(0, 50, 50, 63)) 
  6.     button.setTitle("Open", forState: UIControlState.Normal) 
  7.     button.addTarget(self, action: "buttonPressed:", forControlEvents: UIControlEvents.TouchUpInside) 
  8.   
  9.     view.addSubview(button)         

在設定 preferredContentSize 時,指定的寬度都是無效的,系統(tǒng)會自動將其處理為整屏的寬度,所以扔個 0 進去就好了。在這里添加按鈕時我偷了個懶,本來應該使用Auto Layout 和添加約束的,但是這并不是我們這個 demo 的重點。另一方面,為了代碼清晰明了,就直接上坐標了。

然后添加這個按鈕的 action:

  1. @objc private func buttonPressed(sender: AnyObject!) { 
  2.     extensionContext.openURL(NSURL(string: "simpleTimer://finished"), completionHandler: nil) 

我們將傳遞的 URL 的 scheme 是 simpleTimer,以 host 的 finished 作為參數(shù),就可以通知主體應用計時完成了。然后我們需要在計時完成時調(diào)用 showOpenAppButton 來顯示按鈕,更新 viewDidLoad 中的內(nèi)容:

  1. override func viewDidLoad() {   
  2.     //... 
  3.     if (leftTime > 0) { 
  4.         timer = Timer(timeInteral: NSTimeInterval(leftTime)) 
  5.         timer.start(updateTick: { 
  6.             [weak self] leftTick in self!.updateLabel() 
  7.             }, stopHandler: { 
  8.                 [weak self] finished in self!.showOpenAppButton() 
  9.             }) 
  10.     } else { 
  11.         showOpenAppButton() 
  12.     } 

最后一步是在主體應用的 target 里設置合適的 URL Scheme: 

設置 url scheme

設置 url scheme

然后在 AppDelegate.swift 中捕獲這個打開事件,并檢測計時是否完成,然后做出相應:

  1. func application(application: UIApplication!, openURL url: NSURL!, sourceApplication: String!, annotation: AnyObject!) -> Bool {   
  2.     if url.scheme == "simpleTimer" { 
  3.         if url.host == "finished" { 
  4.             NSNotificationCenter.defaultCenter() 
  5.                 .postNotificationName(taskDidFinishedInWidgetNotification, object: nil) 
  6.         } 
  7.         return true 
  8.     } 
  9.   
  10.     return false 

在這個例子里,我們發(fā)了個通知。而在 ViewController 中我們可以一開始就監(jiān)聽這個通知,然后收到后停止計時并彈出提示就行了。當然我們可能需要一些小的重構(gòu),比如添加是手動打斷還是計時完成的判斷以彈出不一樣的對話框等等,這些都很簡單再次就不贅述了。 

完成

完成

至此,我們就完成了一個很基本的通知中心擴展,完整的項目可以在 GitHub repo 的 master 上找到。這個計時器現(xiàn)在在應用中只在前臺或者通知中心顯示時工作,如果你退出應用后再打開應用,其實這段時間內(nèi)是沒有計時的。因此這個項目之后可能的改進 就是在返回應用的時候添加一下計時的判定,來更新計時器的剩余時間,或者是已經(jīng)完成了的話就直接結(jié)束計時。

其他

其實在 Xcode 為我們生成的模板文件中,還有這么一段代碼也很重要:

  1. func widgetPerformUpdateWithCompletionHandler(completionHandler: ((NCUpdateResult) -> Void)!) {   
  2.     // Perform any setup necessary in order to update the view. 
  3.   
  4.     // If an error is encoutered, use NCUpdateResult.Failed 
  5.     // If there's no update required, use NCUpdateResult.NoData 
  6.     // If there's an update, use NCUpdateResult.NewData 
  7.   
  8.     completionHandler(NCUpdateResult.NewData) 

對于通知中心擴展,即使你的擴展現(xiàn)在不可見 (也就是用戶沒有拉開通知中心),系統(tǒng)也會時不時地調(diào)用實現(xiàn)了 NCWidgetProviding 的擴展的這個方法,來要求擴展刷新界面。這個機制和 iOS 7 引入的后臺機制是很相似的。在這個方法中我們一般可以做一些像 API 請求之類的事情,在獲取到了數(shù)據(jù)并更新了界面,或者是失敗后都使用提供的 completionHandler 來向系統(tǒng)進行報告。

值得注意的一點是 Xcode (至少現(xiàn)在的 beta 4) 所提供的模板文件的 ViewController 里雖然有這個方法,但是它默認并沒有 conform 這個接口,所以要用的話,我們還需要在類聲明時加上 NCWidgetProviding。

總結(jié)

這個 Demo 主要涉及了通知中心的 Toady widget 的添加和一般交互。其實擴展是一個相當大塊的內(nèi)容,對于其他像是分享或者是 Action 的擴展,其使用方式又會有所不同。但是核心的概念,生命周期以及與本體應用交互的方法都是相似的。Xcode 在我們創(chuàng)建擴展時就為我們提供了非常好的模版文件,更多的時候我們要做的只不過是在相應的方法內(nèi)填上我們的邏輯,而對于配置方面基本不太需要操心,這一點 還是非常方便的。

就為了擴展這個功能,我已經(jīng)迫不及待地想用上 iOS 8 了..不論是使用別人開發(fā)的擴展還是自己開發(fā)方便的擴展,都會讓這個世界變得更美好。

原文取自:OneV's Den的博客

責任編輯:閆佳明 來源: onevcat.com
相關推薦

2021-04-23 14:07:55

iOS 15iPhone界面

2012-03-30 10:37:05

數(shù)據(jù)中心擴展服務器

2022-02-10 20:09:24

Dubbo源碼Provider

2013-06-14 11:21:43

iOS開發(fā)移動開發(fā)畫圖

2009-12-04 15:43:03

PHP JSON擴展

2013-06-09 09:55:25

云數(shù)據(jù)中心云連接解決方案

2009-11-16 09:14:57

GoogleChrome擴展中心

2011-07-22 16:47:53

iOS 通知 xcode

2022-05-19 19:14:30

數(shù)據(jù)中心縱向擴展橫向擴展

2022-05-15 15:14:04

爬蟲Requests反爬

2013-07-10 10:02:27

Chrome 28瀏覽器

2011-10-13 15:46:48

通知中心手機

2021-12-06 11:26:05

Windows 11飛行模式通知中心

2017-12-18 11:09:45

消息轉(zhuǎn)發(fā)DemoPerson

2014-07-11 09:33:24

iOS 8動作擴展

2012-03-09 13:52:28

Adob??e AIRiOS

2013-09-12 15:37:09

iOS開發(fā)流程

2009-11-16 10:00:48

谷歌Chrome擴展中心

2012-02-24 09:09:46

微軟數(shù)據(jù)中心云計算

2012-02-24 09:42:48

微軟數(shù)據(jù)中心
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 欧美 日韩 国产 成人 | 91亚洲欧美 | 国产一区二区黑人欧美xxxx | 欧美一区二区久久 | 亚洲一区二区三区免费视频 | 九一在线 | 久久中文字幕电影 | 伊人网伊人网 | 国产综合视频 | 精品亚洲一区二区 | 先锋资源亚洲 | 一级美国黄色片 | 久久久久久久久久影视 | 久久99久久久久 | 日韩欧美精品在线 | 久久久久久久久99 | 成人久久18免费网站 | 欧美精品二区三区 | 久久久精品在线 | 91精品观看 | 国产精品久久久久一区二区三区 | 国产综合第一页 | 欧美xxxx性 | av入口| 日韩久久久久久久久久久 | 国产日韩欧美中文 | 国产精品视频免费观看 | 日韩视频免费看 | 91看片官网 | 毛片一区二区三区 | 日韩一区二区三区在线视频 | 亚洲国产成人精品女人久久久 | 国产精品女人久久久 | 在线观看中文字幕 | 日韩中文字幕免费 | 国产日本精品视频 | 日韩av在线一区 | 91精品国产综合久久婷婷香蕉 | 天天干在线播放 | 久久久国产精品视频 | 亚洲国产aⅴ成人精品无吗 欧美激情欧美激情在线五月 |