揭秘 iOS App Extension 開(kāi)發(fā) —— Today 篇

從 iOS 8 開(kāi)始,蘋(píng)果引入了全新的 App Extension,涉及到方方面面,例如今日面板、鍵盤(pán)、內(nèi)容攔截器、分享動(dòng)作等。但是官方對(duì)于 App Extension 的開(kāi)發(fā)指南少之又少,入門(mén)起來(lái)會(huì)有很多坑。所以我準(zhǔn)備寫(xiě)一系列文章來(lái)幫助大家更好入門(mén) App Extension 的開(kāi)發(fā),也能少走彎路。

何為 App Extension?

顧名思義,它是一種擴(kuò)展,很類似于一些大型軟件(好吧,現(xiàn)在可能是個(gè)應(yīng)用都可以有)的插件機(jī)制。App Extension 事實(shí)上并不是你應(yīng)用的插件,而是系統(tǒng)的插件,其生命周期是由系統(tǒng)來(lái)管理的,所以如果你想做什么壞事還是行不通的...但是 App Extension 分發(fā)的載體是應(yīng)用,也就是說(shuō)如果你只是單純想做一個(gè)今日面板插件,也需要有個(gè)主程序,你的主程序可以什么都不做,也可以提供一些基本的設(shè)置和數(shù)據(jù)。

App Extension 和主程序的關(guān)系?

可以說(shuō)沒(méi)有什么關(guān)系,基本上就是兩個(gè)獨(dú)立的程序,你的主程序既不可以訪問(wèn) App Extension 的代碼,也不可以訪問(wèn)其存儲(chǔ)空間,這完完全全就是兩個(gè)進(jìn)程、兩個(gè)程序。這時(shí)你可能會(huì)問(wèn),我擦,都不能交互那有什么卵用??別急,后面我會(huì)講到如何做一些交互。

App Extension 可以干什么?不可以干什么?

基本上什么都能干,有人不是在今日面板里把 Chrome 的恐龍小彩蛋硬塞進(jìn)去了嗎?還有拿輸入法當(dāng)瀏覽器作分屏多任務(wù)的...只有你想不到,沒(méi)有你做不到......誒等等,有些還是做不到的。比如,內(nèi)存有限制,App Extension 的可用內(nèi)存遠(yuǎn)不如常規(guī)應(yīng)用,以至于如果你真想做游戲,還是掂量掂量你的資源占用問(wèn)題能不能解決吧。而且還不能訪問(wèn) UIApplication,因?yàn)樗娜萜鲬?yīng)用是系統(tǒng),你拿系統(tǒng)的 UIApplication 想干嘛...(當(dāng)然,你可以用遞歸查找 UIResponder 的方法拿到 UIApplication,但是我沒(méi)試過(guò))再次,你不能執(zhí)行長(zhǎng)時(shí)間的操作,你的 App Extension 可能隨時(shí)被系統(tǒng) Kill 掉,who knows?

還有更多不可用的 API 可以看這個(gè)蘋(píng)果官方文檔:Understand How an App Extension Works

開(kāi)始創(chuàng)建一個(gè) App Extension

首先看一下我們要做的東西,是一個(gè)簡(jiǎn)單 Todo 應(yīng)用,主程序長(zhǎng)這個(gè)樣:


今日面板插件長(zhǎng)這個(gè)樣:


界面都很簡(jiǎn)單啦~
主程序?qū)崿F(xiàn)其實(shí)很簡(jiǎn)單,就是 Table View 的使用以及數(shù)據(jù)持久化,這里就不著重講了。但注意,我們要留出一個(gè)接口給今日面板,假設(shè)這里我們要在今日面板里顯示前 4 條待辦事項(xiàng),我們必須要單獨(dú)將這 4 條存在一個(gè)主程序和擴(kuò)展都能訪問(wèn)的地方。后面我會(huì)說(shuō)怎么做。

Tips:


蘋(píng)果的 HIG 明確指出,不要在今日面板里使用可以滾動(dòng)的 Scroll View,而是要完全展開(kāi),因此對(duì)于多條數(shù)據(jù),我們要不就分頁(yè),要不就只顯示前幾項(xiàng)。

下面,我們就為工程創(chuàng)建一個(gè) Today Extension:


一路下一步,輸入一個(gè)子項(xiàng)目名,點(diǎn) 『Finish』就完成 Today Extension 的添加了。

這個(gè)子項(xiàng)目的初始目錄結(jié)構(gòu)如下:


P.S. 那個(gè) entitiements 文件是后來(lái)創(chuàng)建的,一開(kāi)始不會(huì)有

然后我們?cè)?Storyboard 里把大致界面拖出來(lái),如果畫(huà)布大小不合適可以在這調(diào)整一下,但是也就是調(diào)整了預(yù)覽效果,真實(shí)的大小不能在 IB 里修改。


那我們?cè)趺葱薷囊晥D在今日面板里的大小呢?答案是修改 View Controller 的 preferredContentSize 屬性,不理會(huì) width,調(diào)整 height 到合適的大小即可,因?yàn)閷挾瓤偸呛推聊粚挾认嗤摹?/p>

在這個(gè)例子中,我使用 44 * 項(xiàng)目數(shù)量 - 1 來(lái)作為視圖高度,因?yàn)橐粋€(gè)標(biāo)準(zhǔn) Table View Cell 的高度是 44,然后減掉最后一個(gè)條目分割線的高度就是我們理想的合適高度。

主程序向 App Extension 共享數(shù)據(jù)

我們?cè)谥鞒绦蚶飫?chuàng)建了待辦事項(xiàng),怎么才能讓 App Extension 獲取到呢?由于兩者代碼和數(shù)據(jù)都不互通,所以我們 可以理所當(dāng)然的想到用 App Group 來(lái)解決。首先在主程序中創(chuàng)建一個(gè) App Group:

然后在 App Extension 里添加這個(gè) App Group 即可。
這樣,我們就可以用 NSUserDefaults 通過(guò)這個(gè) App Group 交流數(shù)據(jù)了。

還記得我說(shuō)過(guò)要拿出所有數(shù)據(jù)的前四條放到今日面板中展示嗎?下面我們就來(lái)實(shí)現(xiàn)這個(gè)功能:


當(dāng)主應(yīng)用的數(shù)據(jù)變化后就調(diào)用這個(gè)方法來(lái)更新快照數(shù)據(jù)。

下面我們主要來(lái)看 Today Extension 怎么實(shí)現(xiàn),首先看看這兩個(gè)方法:


其中第一個(gè)方法是系統(tǒng)告訴 Extension 需要更新了,當(dāng)你更新完畢之后通過(guò) block 回調(diào)告訴系統(tǒng)你完成了還有做了什么,通常我們就告訴系統(tǒng)我們更新數(shù)據(jù)了即可(就是給 block 傳 NCUpdateResultNewData 枚舉項(xiàng)作為參數(shù))。

其中第二個(gè)方法是返回一個(gè)內(nèi)補(bǔ)大小,如果不實(shí)現(xiàn),默認(rèn)情況視圖左側(cè)會(huì)有一定的縮進(jìn)。當(dāng)然,蘋(píng)果還是希望你不要修改默認(rèn)的內(nèi)補(bǔ)~

然后我們實(shí)現(xiàn)數(shù)據(jù)的讀?。?/p>

P.S. 第三行寫(xiě)錯(cuò)了,不要管它
其實(shí)也很簡(jiǎn)單,就是從 App Group 的配置里拿出前 4 項(xiàng)的快照,然后更新一下 Table View 即可。這個(gè)方法在 viewDidLoad 或者 widgetPerformUpdateWithCompletionHandler: 中調(diào)用都可以。

到這我們看看效果,選中 Today Extension 的那個(gè) Scheme 點(diǎn)擊調(diào)試按鈕,彈出下面的對(duì)話框:



選擇我們的主程序,點(diǎn)擊 『Run』。

App Extension 調(diào)起主程序并執(zhí)行動(dòng)作

當(dāng)我們的 Todo List 是空的情況下,我們希望在今日面板里展示一個(gè)按鈕,點(diǎn)擊后可以快速進(jìn)入創(chuàng)建 Todo 的界面,就像這樣:


由于我們?cè)L問(wèn)不了主程序的代碼,所以只剩下一條路可以選,那就是 URL Scheme。

首先,我們給主程序注冊(cè)一個(gè) URL Scheme:


然后響應(yīng)按鈕點(diǎn)擊:


由于 App Extension 訪問(wèn)不了 UIApplication,因此不能用它的 openURL:,但是我們可以用 extensionContext 來(lái)打開(kāi) URL,用法和效果是一樣的。

回到主程序,我們處理 URL 的打開(kāi):


這里我用 Notification 的方式告知指定 View Controller 來(lái)執(zhí)行相應(yīng)動(dòng)作,當(dāng)然你也可以用你自己喜歡的方式,這里最復(fù)雜也就是處理路由,現(xiàn)在也有很多方法實(shí)現(xiàn),我這里就不深究了。

下面看看效果(不好意思,圖沒(méi)做好,不動(dòng)請(qǐng)?jiān)谛麓翱谥写蜷_(kāi)):


好了,到這我們就基本打通主程序和 App Extension 的相互通信了,是不是也很簡(jiǎn)單呢?

最后,一個(gè)小提醒

由于通知中心的界面是一大塊 UIVisualEffectView ,并且具體參數(shù)調(diào)整過(guò),所以插件的背景色最好保持透明,主要文字顏色最好是白色,次要文字的顏色最好是 lightTextColor,這樣能適應(yīng)毛玻璃下的 Vibrancy 效果。

今日面板每個(gè)插件的高度計(jì)算和 UITableView 自適應(yīng)高度的計(jì)算方式一致,如果你沒(méi)有設(shè)置 preferredContentSize,或者把它設(shè)為 CGSizeZero 了,就表示你想采用自適應(yīng)高度,那么系統(tǒng)就會(huì)根據(jù)你設(shè)計(jì)的 Auto Layout 來(lái)確定適合的高度。如果你想這么做的話,直接參考 UITableViewCell 在 iOS 8 以后自適應(yīng)高度的方式即可。

完了~希望大家支持!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容