從 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ò)展蒲列,很類(lèi)似于一些大型軟件(好吧窒朋,現(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ú)立的程序鹏往,你的主程序既不可以訪(fǎng)問(wèn) App Extension 的代碼,也不可以訪(fǎng)問(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)題能不能解決吧。而且還不能訪(fǎng)問(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ò)展都能訪(fǎng)問(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è)條目分割線(xiàn)的高度就是我們理想的合適高度。
主程序向 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.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ì)話(huà)框:
選擇我們的主程序搭儒,點(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 訪(fǎng)問(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)確定適合的高度慌洪。如果你想這么做的話(huà)俺抽,直接參考 UITableViewCell
在 iOS 8 以后自適應(yīng)高度的方式即可。
完了~希望大家支持莫湘!