iOS之構(gòu)建Widget

伴隨這iOS 8 系統(tǒng)多達(dá)4000項API更新而來同樣還有Today Extension.而對iOS而言外构,有了Today Extension 開發(fā)者可以很好借助系統(tǒng)提供的接入點為系統(tǒng)定制的服務(wù),提供自定義的附加功能.這意味著什么呢程储?從iOS 7版本嘗試開路到現(xiàn)在iOS 8更新的到來終于向開發(fā)者開放Widget接入,這意味著系統(tǒng)應(yīng)用和第三方應(yīng)用都可以通知中心(Notification Center)里面實現(xiàn)交互.

Notification Center Widget [Via Apple]

其實相對于Android,因其特有開放性Widget插件已經(jīng)發(fā)展了很多年椿争,擁有極高自由定制性,在新版本的Android系統(tǒng)中甚至可以將部分插件擺在鎖屏頁.而Google和各大軟件廠商制作的Widget插件也能很好與系統(tǒng)的整體風(fēng)格進(jìn)行無縫的融合,而直到目前iOS 8版本中鹦筹,Widget也就只是能擺在通知中心(Notification Center)今天通知欄中而已夜畴,相對于Android也聽到很多人把這個作為"iOS不夠開放"一個有力的依據(jù).針對這個問題其實Apple也在iOS Human Interface Guidelines中提到:

iOS 8 中開發(fā)者的中心并不應(yīng)該發(fā)生改變拖刃,依然應(yīng)該是圍繞 app.在 app 中提供優(yōu)秀交互和有用的功能,現(xiàn)在是贪绘,將來也會是 iOS 應(yīng)用開發(fā)的核心任務(wù)兑牡。而Widget在 iOS 中是不能以單獨的形式存在的,一定是隨著一個應(yīng)用一起打包提供的。

從這個側(cè)面可見税灌,Apple對開放一直持有審慎的態(tài)度均函,開放的目的是力求保證整體體驗完整性,雖然iOS的Widget相比Android自定義性太低菱涤,但基于Apple目前的開放程度而言是能夠很有效控制Widget與系統(tǒng)的更好的融合.雖似戴著鐐銬起舞苞也,但卻能捕獲人心.

而從用戶角度來看,在無需打開應(yīng)用前提下就可以對消息進(jìn)行處理的交互特性,使它在很多場景里有效提升了用戶操作效率.例如在Widget中快速回復(fù)email粘秆,即時完成Todo日程等.這種交互更多從更宏觀角度重新定義了消息,通知中心(Notification Center)通過獲取用戶上一行為如迟,還可以起到承接下一行為的作用(雖然目前開放API只能做到系統(tǒng)級的行為).點雖小,但這對用戶使用習(xí)慣改變卻是巨大的.

Widget on hands [Via Yalantis]

有人看到這肯定一定會問為何沒有提到Windows Phone平臺?因為無論從通知中心快捷入口數(shù)量還是談到可以交互的點一句話而概之WP的現(xiàn)狀是“一窮二白”攻走,你想作為曾經(jīng)走過WP7時代用戶根本不知道通知中心為何物的殷勘,而是用了足足兩年時間WP8上才有體現(xiàn),而那些被其他平臺玩膩的希望習(xí)以為常通知中心交互陋气,就像這樣:

WP 通知中心[Via PCGGroup]

你就像看這張靜態(tài)圖片一樣也就是停留只是看看程度而已(除了刪除操作之外),MS針對通知中心現(xiàn)在最新消息是未來會支持類似可以通知中心直接回復(fù)短信等交互劳吠,至于什么時候能夠等到,誰知道呢.

說了這么多巩趁,回歸正題.

1.交互

在開始構(gòu)建Widget之前痒玩,如果想對Widget實現(xiàn)技術(shù)細(xì)節(jié)和交互特點有一個完整概覽淳附,我覺得沒有什么文檔比官方App Extension Programming Guide更值得一讀了.剛開始接觸iOS通知中心,一直很疑惑為何通知中心采用兩個不同Tab“今日”和“通知”來對消息進(jìn)行分離.其實這和Widget工作機(jī)制有關(guān).

Widget是放在“今日”Tab之中蠢古,而它工作機(jī)制是只有用戶下拉通知中心時才會去刷新獲取最新數(shù)據(jù)奴曙,這種做法和Android不同在于,Android更偏向于把整個Widget一直放在后臺實時持續(xù)的更新.設(shè)想一下草讶,如果我們看同樣天氣信息洽糟,Android會持續(xù)消耗資源去做一件用戶不會實時預(yù)覽信息,這也就能解釋為何經(jīng)常看到Android用戶抱怨耗電問題.而對于即時消息堕战,iOS做法是直接把這些消息實時歸類到”通知“Tab中.其實這種做法很好解決采用消耗最少資源前提下保證其操作的靈活性.

因為現(xiàn)有Widget一般來說是展現(xiàn)在系統(tǒng)級別的 UI上坤溃,所以在App Extension Programming Guide中Apple對Widget交互提出如下明確的要求:

擴(kuò)展應(yīng)該保持輕巧迅速,并且專注功能單一嘱丢,在不打擾或者中斷用戶使用當(dāng)前應(yīng)用的前提下完成自己的功能點.

類似一直摯愛Todo應(yīng)用Clear則交互上堪稱上典范:

Clear's Widget

當(dāng)然如果動點腦子會發(fā)現(xiàn)薪介,Widget開放iOS上實現(xiàn)應(yīng)用之間Launcher成為了可能,類似早期一直很魔性應(yīng)用"Launcher":

Launcher's Widget

可以讓用在 iOS 的通知中心里越驻,以類似應(yīng)用程序捷徑的方式直接快速切換 App 的小工具汁政,其實當(dāng)初在推出沒多久后,便被 Apple 以"誤用 / 濫用"Widgets 為理由下架缀旁,但有意思的就在幾天前3月20日又重新上架.

2.構(gòu)建

在Widget技術(shù)實現(xiàn)細(xì)節(jié)上记劈,并不打算在本篇把所有技術(shù)細(xì)節(jié)通覽一遍,我只會寫我個人(其實就是初學(xué)者)認(rèn)為值得寫的容易出錯的點或者耗費一些時間找到一些問題的解決方案.

2.1 純代碼構(gòu)建

Xcode 6中已經(jīng)支持Today Extension創(chuàng)建Widget的模板并巍,該模板會默認(rèn)創(chuàng)建MainInterface.storyboard文件來構(gòu)建UI:

StoryBoard UI

當(dāng)然對于一個純代碼的擁躉而言目木,肯定直接刪除storyboard文件采用純代碼方式來進(jìn)行構(gòu)建,刪除完后之后注意需要找到Supporting Files下面的Info.plist中NSExtension字段做如下兩個操作:

A:直接刪除NSExtensionMainStoryboard字段

B:添加NSExtensionPrincipalClass字段 并設(shè)為TodayViewController

如下:

修改后

注意當(dāng)采用Xcode默認(rèn)模板創(chuàng)建Widget時會自動把ViewController文件命名設(shè)置為“TodayViewController”.當(dāng)然這個ViewController命名其實是可以修改的履澳,唯一值得注意的修改該ViewController文件命名后還需要設(shè)置NSExtensionPrincipalClass的值與其保持一致即可.不然Widget編譯時會報找不到對應(yīng)入口.

2.2 左側(cè)間隔

當(dāng)?shù)谝淮翁砑覷I元素采用真機(jī)來運行Widget會發(fā)現(xiàn)嘶窄,Widget左側(cè)到屏幕之間始終會有一段距離的間隔怀跛,導(dǎo)致調(diào)整布局和效果圖差距甚遠(yuǎn)距贷,類似這樣:

左側(cè)間隔

其實這個問題主要是因為Widget里面的視圖默認(rèn)居左居下都會有一定距離的間隔,可以采用如下方式取消間隔吻谋,使布局區(qū)域填充整個Widget:

取消間隔

這種方式把整個布局填充區(qū)域間隔都設(shè)置為0忠蝗,當(dāng)然更簡潔的方式是你可以直接采用“return UIEdgeInsetsZero;”方式.而關(guān)于Widget上布局處理則采用Masonry框架做的相對布局,簡單快捷推薦.當(dāng)然關(guān)于Masonry框架快速上手則不得不推薦閱讀Masonry介紹與使用實踐(快速上手Autolayout).

2.3 整個點擊區(qū)域?qū)崿F(xiàn)

如你所看當(dāng)用戶拉開Widget時,因為Widget是依賴于應(yīng)用程序在分發(fā)時是跟應(yīng)用程序一塊打包的漓拾,希望點擊Widget布局任何區(qū)域都能喚起主應(yīng)用程序,常用的方式在整個View增加Tap事件訂閱處理:

Tap事件

但這種方式會額外產(chǎn)生一個問題阁最,如果Widget空白區(qū)域沒有任何UI元素則無法觸發(fā)該事件,那這里有一個小技巧可以解決改問題,可以整個Widget增加一個透明的ImageView:

設(shè)置透明度

初始化時注意把imageview透明度設(shè)置為0.01最小值,那么無論設(shè)置其背景色為什么值肉眼都是不可見的.然后使用Masonry框架布局來填充Widget整個背景如下:

填充整個背景

然后為imageview增加Tap事件訂閱即可:

增加事件訂閱

這樣就能整個Widget區(qū)域可點擊效果.另外針對通過Widget中喚起主應(yīng)用程序方式目前只支持url scheme方式來實現(xiàn).同時也是Widget向主應(yīng)用程序反饋數(shù)據(jù)和交互的渠道之一.

2.4 定時更新機(jī)制

Widget自身更新機(jī)制當(dāng)用戶下拉通知中心(Notification Center)時立即更新數(shù)據(jù)骇两,但我們仔細(xì)研究Widget用戶使用場景時發(fā)現(xiàn)速种,如果用戶鎖屏?xí)r間過長,打開Widget后不做任何操作低千,這個時候針對一些即時類應(yīng)用配阵,類似我們天氣中可能涉及到災(zāi)害預(yù)警它要求場景數(shù)據(jù)一旦產(chǎn)生就要實時展現(xiàn)給用戶,這就需要我們基于Widget自身機(jī)制外還要處理這個場景下天氣數(shù)據(jù)自動更新的問題.

這個時候我們需要構(gòu)建一個定時更新的NSTimer:

初始化NSTimer

非常簡單,在NSTimer固定更新間隔執(zhí)行的方法調(diào)用就是更新數(shù)據(jù)方法棋傍,當(dāng)然重點不在這里救拉,而是觸發(fā)和關(guān)閉這個NSTimer時機(jī).按照Widget生命周期來說,如果用戶是第一次下拉查看Widget其實就是執(zhí)行整個ViewController生命周期調(diào)用過程瘫拣,這個并沒有什么問題亿絮,但是還是存在一個特殊情況.系統(tǒng)為了保證Widget上數(shù)據(jù)是及時更新的,默認(rèn)會截取上次顯示成功Widget的快照.這個快照會一直保存到新的數(shù)據(jù)或UI被更新才回被替換,那這就會帶來一個問題,當(dāng)你拖拽通知中心(Notification Center)下拉過于頻繁時麸拄,Debug跟蹤代碼執(zhí)行路徑你會發(fā)現(xiàn)整個Widget生命周期執(zhí)行過程和第一次下拉執(zhí)行的路徑發(fā)生了變化.

第一次下拉執(zhí)行路徑是viewDidLoad->viewWillAppear,而如果下拉過于頻繁你就會發(fā)現(xiàn)代碼執(zhí)行路徑直接只會執(zhí)行viewWillAppear方法派昧,這個就是系統(tǒng)默認(rèn)保存上次快照而導(dǎo)致的執(zhí)行路徑上變化.這對我們選擇NSTimer更新時機(jī)以及后面會提到的Widget橫豎屏處理都會有影響.

那么很明顯,為了保證這個定時更新機(jī)制能夠無論用戶什么情況下操作都能起作用拢切,我們需要把NSTimer fire觸發(fā)代碼調(diào)用放到viewWillAppear方法中來.同理當(dāng)Widget關(guān)閉后在viewDidDisappear方法取消NSTimer invalidate定時更新即可.

2.5 Widget橫屏支持

關(guān)于Widget橫屏支持在開發(fā)中耽誤一點時間來解決這個問題,在iPhone 6 & Plus上已經(jīng)橫豎屏直接切換斗锭,Widget默認(rèn)是豎屏,但如果你需求中橫屏UI的布局和豎屏布局完全不同失球,這個時候你就需要判斷當(dāng)前Widget橫豎屏狀態(tài)來切換對應(yīng)的布局.

當(dāng)然一般思路我們都會按照端內(nèi)處理橫豎屏方式來處理Widget岖是,如果你翻過官方的開發(fā)文檔,你會發(fā)現(xiàn)在iOS 6.0版本之前UIViewController之間橫豎屏切換实苞,只需要設(shè)置shouldAutorotateToInterfaceOrientation函數(shù)即可.UIInterfaceOrientation是UIApplication.h頭文件中定義的枚舉類型豺撑,總共有四個方向.在shouldAutorotateToInterfaceOrientation方法中返回相應(yīng)的結(jié)果即可,如果直接返回YES將支持所有方向.而在iOS 6.0版本之后黔牵,UIViewController之間橫豎屏切換需要多設(shè)置一個supportedInterfaceOrientations函數(shù)返回UIInterfaceOrientationMask枚舉類型.除了設(shè)置shouldAutorotateToInterfaceOrientation之外,還要將supportedInterfaceOrientations返回的方向與shouldAutorotateToInterfaceOrientation保持一致聪轿,否則會在兩個支持不同橫豎屏ViewController中切換時,會出現(xiàn)豎屏變橫屏猾浦,橫屏變豎屏的情況.但問題是這種方式是否適用Widget橫屏處理呢?

使用UIDeviceOrientationIsPortrait來判斷:

判斷橫屏方法一

當(dāng)你執(zhí)行這段代碼調(diào)試時你會發(fā)現(xiàn)陆错,orientation方向的值始終都會是UIDeviceOrientationUnknown.如果你點開UIDeviceOrientation枚舉你會看到.它包含了兩個扁平方向UIDeviceOrientationFaceUp和UIDeviceOrientationFaceDown,其實它代表的意思屏幕朝上或朝下平躺兩個方向的判斷.所以當(dāng)你設(shè)備平躺桌面時.即時你有時已經(jīng)切換了橫屏你會發(fā)現(xiàn)它會返回FaceUp或FaceDown金赦,所以你當(dāng)你調(diào)用UIDeviceOrientationIsPortrait方法時它返回值其實是沒有意義的音瓷,因為設(shè)備目前方向在平躺下Faceup和FaceDown既不是橫屏也不是豎屏.難道沒有更好的方式嘛?

可以采用如下方式能夠完美解決Widget橫豎屏切換狀態(tài)判斷的問題:

Widget橫豎屏狀態(tài)判斷

其實設(shè)置Widget顯示高度時就會發(fā)現(xiàn),高度在橫豎屏狀態(tài)切換是不會變化的,但寬度會隨著橫豎屏狀態(tài)切換會發(fā)生變化,所以判斷屏幕寬度這個思路是可取的.因為橫豎屏UI布局不同摆霉,調(diào)用時機(jī)則可以選擇在viewWillLayoutSubviews或viewDidLayoutSubviews方法中進(jìn)行.因為這兩個方法都是viewWillAppear方法是必然執(zhí)行的徘溢,這也就自然規(guī)避Widget自身因為下拉快照保存機(jī)制導(dǎo)致代碼執(zhí)行路徑變化導(dǎo)致布局更新的問題.

2.6 Widget國際化

在來說說這個Widget國際化,因為我們客戶端自身已經(jīng)支持三種不同語言,這就是導(dǎo)致Widget也是需要根據(jù)端內(nèi)語言變化必須有國際化的支持.其實我們端內(nèi)已經(jīng)做了一套完整的國際化機(jī)制.Widget最好處理方式能夠復(fù)用端內(nèi)機(jī)制,而不需要單獨開發(fā)支持.iOS 8 新引入的自制 framework 的方式來組織需要重用的代碼,這樣在鏈接 framework 后 app 和Widget就都能使用相同的代碼. 包含Widget中數(shù)據(jù)請求和數(shù)據(jù)記憶其他能夠復(fù)用的代碼。

這也是我們一開始打算解決方式珊楼,但發(fā)現(xiàn)剝離這部分代碼時間周期明顯超過我們預(yù)期.所以在國際化處理上我們Widget獨立做了一套國際化處理,它和端內(nèi)在處理機(jī)制上并沒有多大的不同:

Widget國際化處理

當(dāng)然重點不再于它的實現(xiàn)度液,你可以發(fā)現(xiàn)我們Widget中國際化文本文件Locallizable.string命名加了一個"WG",這個問題是剛開始開發(fā)之初我們一直認(rèn)為Widget作為端是獨立于主應(yīng)用程序的.所以當(dāng)初理解為只有把這個文件命名為的“Locallizable.string”才是正常的能夠被識別的厕宗,但我們調(diào)試時發(fā)現(xiàn)邓了,Widget打包時會把這些國際化單獨放到PlugIns文件下,這里給出一個簡體中文全路徑:

/private/var/mobile/Containers/Bundle/Application/61C637FF-B5BC-432A-ADD5-BA64EBFE98E8/MojiWeather.app/PlugIns/MojiWidget.appex/zh-Hans.lproj

根據(jù)這個路徑你會發(fā)現(xiàn)文件時可以找到的,但調(diào)試時發(fā)現(xiàn)國際化取對應(yīng)Key的值一直是取不到的媳瞪,但我們?nèi)我夥恰癓ocallizable.string”時則是沒有問題的骗炉,后來我們發(fā)現(xiàn)當(dāng)我們打包在不同機(jī)型上測試這個問題時,如果“Locallizable.string”名稱命名會導(dǎo)致調(diào)試時ok蛇受,而最終打包上會出現(xiàn)找不到對應(yīng)key值得問題.這個原因到我寫這篇blog一直沒有找到具體的原因.所以我們給出解決方案是一定要和主應(yīng)用程序“Locallizable.string”保持不同即可解決.

當(dāng)然關(guān)于Widget中閃現(xiàn)的問題句葵,因為我們Widget存在兩個不同尺寸切換,導(dǎo)致這個問題很明顯兢仰,處理方式自然是viewWillLoad方式中做好Widget高度在不同場景高度初始化就可以完美避免.這里就不做贅述.

文/chenkai(簡書作者)

原文鏈接:http://www.reibang.com/p/0efd62ee033a

著作權(quán)歸作者所有乍丈,轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),并標(biāo)注“簡書作者”把将。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轻专,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子察蹲,更是在濱河造成了極大的恐慌请垛,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洽议,死亡現(xiàn)場離奇詭異宗收,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)亚兄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門混稽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人审胚,你說我怎么就攤上這事匈勋。” “怎么了膳叨?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵洽洁,是天一觀的道長。 經(jīng)常有香客問我懒鉴,道長诡挂,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任临谱,我火速辦了婚禮,結(jié)果婚禮上奴璃,老公的妹妹穿的比我還像新娘悉默。我一直安慰自己,他們只是感情好苟穆,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布抄课。 她就那樣靜靜地躺著唱星,像睡著了一般。 火紅的嫁衣襯著肌膚如雪跟磨。 梳的紋絲不亂的頭發(fā)上间聊,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機(jī)與錄音抵拘,去河邊找鬼哎榴。 笑死,一個胖子當(dāng)著我的面吹牛僵蛛,可吹牛的內(nèi)容都是我干的尚蝌。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼充尉,長吁一口氣:“原來是場噩夢啊……” “哼飘言!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起驼侠,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤姿鸿,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后倒源,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體般妙,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年相速,在試婚紗的時候發(fā)現(xiàn)自己被綠了碟渺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡突诬,死狀恐怖苫拍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情旺隙,我是刑警寧澤绒极,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站蔬捷,受9級特大地震影響垄提,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜周拐,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一铡俐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧妥粟,春花似錦审丘、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锅知。三九已至,卻和暖如春脓钾,著一層夾襖步出監(jiān)牢的瞬間售睹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工可训, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留昌妹,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓沉噩,卻偏偏與公主長得像捺宗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子川蒙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 伴隨這iOS 8 系統(tǒng)多達(dá)4000項API更新而來同樣還有Today Extension.而對iOS而言蚜厉,有了To...
    chenkai閱讀 13,499評論 21 99
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件畜眨、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,024評論 4 62
  • 我要從南走到北我還要從白走到黑 我要人們都看到我但不知道我是誰 假如你看我有點累就請你給我倒碗水 假如你已經(jīng)愛上我...
    柳夢梅閱讀 330評論 0 0
  • 花 昨日與兩友閑聊昼牛,其中一友天生麗質(zhì),溫婉動人康聂,膚白如雪贰健,吹彈可破。而另一友從容貌上卻是相差甚遠(yuǎn)恬汁,無法匹敵伶椿。當(dāng)我們...
    藝花閱讀 255評論 0 1
  • 曾無數(shù)次想象相見的情景约巷, 但見了面偎痛, 卻連一秒的直視都尷尬, 年少深愛的人独郎, 心動過踩麦, 但也深深失望。 青春里氓癌, ...
    稚映閱讀 360評論 0 0