iOS today

1.擴(kuò)展是什么?

擴(kuò)展是一個(gè)特殊的程序。但是它并不屬于一個(gè)完整的APP,它需要有一個(gè)容器APP(containing app)來進(jìn)行發(fā)布。容器可以是一個(gè)已經(jīng)存在的APP,也可以創(chuàng)建一個(gè)新的。一個(gè)容器可以有一個(gè)或一個(gè)以上的擴(kuò)展。擴(kuò)展不能獨(dú)立的進(jìn)行發(fā)布,它需要有一個(gè)容器。

擴(kuò)展被它所屬的宿主程序(host app)所加載和控制。舉個(gè)例子,它可以像Safari一樣有分享擴(kuò)張,或者像通知中心的今日摘要和其他組件一樣。系統(tǒng)的每一個(gè)支持?jǐn)U展的地方叫做擴(kuò)展插入點(diǎn)。

為了創(chuàng)建擴(kuò)展,你需要添加一個(gè)target到容器(cotaining app)的工程文件之中。Xcode提供的模板已經(jīng)包括擴(kuò)展接入點(diǎn)所需要的框架,

2.今日擴(kuò)展接入點(diǎn)

擴(kuò)展提供了今日擴(kuò)展接入點(diǎn),就是所謂的組件,可以提供簡(jiǎn)單快捷的訪問信息。組件關(guān)聯(lián)到通知中心。設(shè)計(jì)一個(gè)簡(jiǎn)單易用的的組件是非常重要的,因?yàn)樘嗟慕换タ赡軙?huì)導(dǎo)致用戶的操作困難。要注意千萬不要使用鍵盤。

我們都希望組件能夠有好看的界面和及時(shí)更新,保證它穩(wěn)定的工作尤為重要,你需要讓你的組件又快又節(jié)省資源。避免影響這個(gè)用戶體驗(yàn),系統(tǒng)將終止組件的運(yùn)行如果組件占用了太多內(nèi)存。組件需要的是簡(jiǎn)單,專注于顯示他們需要顯示的內(nèi)容。

理論說夠了,讓我們來創(chuàng)建一個(gè)自己的今日組件,我們用它來顯示磁盤的用量,通過一個(gè)進(jìn)度條來提示用戶。在這個(gè)過程中,我們也會(huì)涉及其他的擴(kuò)展。

3.實(shí)現(xiàn)步驟

步驟1:創(chuàng)建項(xiàng)目

如果你想要?jiǎng)?chuàng)建一個(gè)擴(kuò)展作為一個(gè)已經(jīng)存在的app,打開Xcode工程,然后到步驟2。如果你像我一樣從零開始,你需要先創(chuàng)建一個(gè)容器程序(containing app)。

打開Xcode在File目錄選擇New > Project...我們使用objective-C作為開發(fā)語言,選擇Single View Application模板。

步驟2:添加一個(gè)target

打開File目錄,選擇New > Target....在Application Extension的類別中選擇Today Extension模板。

你會(huì)注意到target被添加到我們當(dāng)前的工程,擴(kuò)展將會(huì)被嵌入到容器程序。然后擴(kuò)展會(huì)有一個(gè)標(biāo)識(shí)符類似容器程序com.tutsplus.Today.Used-Space.

點(diǎn)擊Next, 給組件取個(gè)名字,例如Used Space, 點(diǎn)擊Finish創(chuàng)建一個(gè)target..,Xcode將為你創(chuàng)建一個(gè)新的scheme,然后詢問你是否激活,點(diǎn)擊Activate繼續(xù)。

Xcode將為組件創(chuàng)建一個(gè)名叫Space Used的分組 然后在其中生成一些文件,一個(gè)UIViewController和一個(gè)storyboard,組件就只是一個(gè)UIViewController和一個(gè)storyboard,如果你打開視圖控制器的頭文件,你會(huì)發(fā)現(xiàn)它就是一個(gè)普通的UIViewController的子類。

如果你選擇擴(kuò)展的target,打開Build Phases并展開Link Binary With Libraries,你會(huì)發(fā)現(xiàn)它添加了Notification Centre得框架。

4.用戶界面

我們現(xiàn)在來創(chuàng)建一個(gè)基本的用戶界面,首先需要確定組件的大小,有兩種方式來告訴系統(tǒng)我們所需的大小。第一是是采用自動(dòng)布局(Auto Layout),第二是使用viewcontroller的preferredContentSize屬性。

自動(dòng)布局的概念在組件上依然是可用的,不僅需要注意iPhone的各種寬度(以及iPad和未來的設(shè)備)也需要注意它可能會(huì)在橫屏模式下現(xiàn)實(shí)。如果界面是用的是自動(dòng)布局來約束,對(duì)開發(fā)者來說是非常方便的。調(diào)節(jié)組件的高度可以通過setPreferredContentSize來實(shí)現(xiàn)。

步驟1:添加元素

在Xcode編輯器中打開MainInterface.storyboard. 你會(huì)發(fā)現(xiàn)已經(jīng)有一個(gè)HelloewWorld的label已經(jīng)在視圖中了,選中并刪除它,因?yàn)槲覀儾粫?huì)用到,添加一個(gè)新的label并且讓它靠右對(duì)齊像圖中那樣。

在Attributes Inspector中設(shè)置顏色為白色, 字體對(duì)齊方式為靠右對(duì)齊, 文本占 50.0%。

在編輯器中選擇Size to Fit Content來讓label自動(dòng)調(diào)節(jié)大小。

接著添加一個(gè)UIProgressView對(duì)象在label的左邊位置,如圖所示。

選中進(jìn)度條,在Attributes Inspector改變Progress Tint為白色, 改變Track Tint為深褐色.這將會(huì)使得進(jìn)度條更加明顯,接下來添加一些約束。

步驟2:添加約束

選中l(wèi)abel添加如圖所示的上下約束。不要勾選Constrain to margins的選項(xiàng)。

選擇進(jìn)度條添加如圖所示的上左右的約束,同時(shí)不要忘記反選Constrain to margins。

因?yàn)槲覀兏淖兞思s束,所以我們會(huì)有一個(gè)小問題需要解決,現(xiàn)在的進(jìn)度條大小并不能正確的反應(yīng)約束后的進(jìn)度條大小,選擇進(jìn)度條, 點(diǎn)擊Resolve Auto Layout Issues按鈕,選擇Update Frames選擇Selected Views.這將會(huì)根據(jù)約束來更新進(jìn)度條的大小。

步驟3:構(gòu)建與運(yùn)行

是時(shí)候來看一下我們的組件了,選擇Used Space scheme,選擇Prodect目錄中得Run或者直接按下Command-R,下拉顯示通知中心,然后點(diǎn)擊下方的edit按鈕,你的今日組件將會(huì)出現(xiàn)在可添加的地方,點(diǎn)擊它左邊的添加按鈕。

這就是我們的擴(kuò)展的樣子。

看起來不錯(cuò),但是為什么在下面有那么多空隙呢?為什么操作系統(tǒng)沒有遵循進(jìn)度條的約束呢?

這個(gè)問題都是操作系統(tǒng)的標(biāo)準(zhǔn)間距導(dǎo)致的,我們接下來將會(huì)改變它,請(qǐng)注意,無論如何,保證進(jìn)度條的左邊距與名字對(duì)齊的。

如果你選擇你的設(shè)備或者在其它設(shè)備運(yùn)行,你會(huì)發(fā)現(xiàn)組件會(huì)調(diào)整大小,這得益于我們使用了自動(dòng)布局( Auto Layout)。

步驟4:修正按鈕邊距

打開TodayViewController.m. 可以看到控制器實(shí)現(xiàn)了NCWidgetProviding協(xié)議. 這意味著我們需要實(shí)現(xiàn)widgetMarginInsetsForProposedMarginInsets:方法來返回一個(gè)自定義的邊距(UIEdgeInsets結(jié)構(gòu)),修改方法如下。

再次運(yùn)行程序查看結(jié)果,發(fā)現(xiàn)組件的下邊距變小了,你可以定制這些邊距達(dá)到任意你想要的效果。

步驟5:連接Outlets

最后,我們來添加兩個(gè)Outlets,打開storyboard,切換到雙編輯器模式,確?,F(xiàn)實(shí)TodayViewController.m。

按住Control并拖動(dòng)label到viewcontroller的接口部分來創(chuàng)建label的接口,命名為percentLabel,重復(fù)這個(gè)過程為UIProgressView創(chuàng)建barView。

5.顯示真實(shí)數(shù)據(jù)

我們將使用NSFileManageer類來計(jì)算設(shè)備的可用空間,那么我們改如何更新組件的數(shù)據(jù)呢?

這就就是NCWidgetProviding協(xié)議的另外一個(gè)方法。操作系統(tǒng)將調(diào)用widgetPerformUpdateWithCompletionHandler:方法當(dāng)組件被載入的時(shí)候或者從后臺(tái)進(jìn)入的時(shí)候。即使組件不可見,系統(tǒng)還是有可能載入和更新它并保存快照,快照將在組件下次出現(xiàn)的時(shí)候顯示,這是因?yàn)樵诮M件顯示之前會(huì)有一小段的時(shí)間。

通過處理完成句柄來判斷是否需要調(diào)用新的內(nèi)容或數(shù)據(jù)更新。block有一個(gè)參數(shù)NCUpdateResult來描述是否我們要更新內(nèi)容。如果不是的話,操作系統(tǒng)會(huì)知道沒有必要保存一個(gè)新的快照。

步驟1:定義屬性

我們需要先定義一些屬性來保存剩余、已使用和總得空間,把這些屬性添加到TodayViewController.m。

步驟2:實(shí)現(xiàn)updateSizes

創(chuàng)建一個(gè)輔助方法updateSizes來拉取必要的數(shù)據(jù)和計(jì)算設(shè)備的空間使用率。

- (void)updateSizes

{

// Retrieve the attributes from NSFileManager

NSDictionary *dict = [[NSFileManager defaultManager]

attributesOfFileSystemForPath:NSHomeDirectory()

error:nil];

// Set the values

self.fileSystemSize = [[dict valueForKey:NSFileSystemSize]

unsignedLongLongValue];

self.freeSize? ? ? = [[dict valueForKey:NSFileSystemFreeSize]

unsignedLongLongValue];

self.usedSize? ? ? = self.fileSystemSize - self.freeSize;

}

步驟3:存儲(chǔ)

我們可以方便的使用NSUserDefaults來存儲(chǔ)數(shù)據(jù)。組件的生命周期很短,所以我們需要緩存這個(gè)值,我們可以給界面設(shè)置一個(gè)初始的值,然后再計(jì)算真實(shí)的值。

這對(duì)我們是否要更新組件的快照是非常有幫助的,讓我們來創(chuàng)建一個(gè)快捷方法來訪問NSUserdefaults。

注意我們使用了一個(gè)宏定義RATE_KEY,別忘了把它加入TodayViewController.m。

步驟4:更新界面

因?yàn)槲覀兊慕M件是一個(gè)視圖控制器,viewDidLoad方法很適合用來更新用戶界面,我們創(chuàng)建一個(gè)幫助方法updateInterface來更新界面。

步驟5:調(diào)用完成句柄

可用空間的變化導(dǎo)致跟新的過于頻繁,為了判斷我們是否真的需要刷新界面,我們計(jì)算已使用量并且設(shè)置了一個(gè)更新的下限0.1%,而不是一改變就刷新界面。修改widgetPerformUpdateWithCompletionHandler:如下:

- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler

{

[self updateSizes];

double newRate = (double)self.usedSize / (double)self.fileSystemSize;

if (newRate - self.usedRate < 0.0001) {

completionHandler(NCUpdateResultNoData);

} else {

[self setUsedRate:newRate];

[self updateInterface];

completionHandler(NCUpdateResultNewData);

}

}

在我們每次重新計(jì)算之后,如果與上次有明顯的變化,則保存值并更新界面,告訴操作系統(tǒng)。如果沒有明顯的變化,我們就不需要新的快照。如果有錯(cuò)誤發(fā)生將會(huì)在NCUpdateResultFailed來報(bào)告錯(cuò)誤的發(fā)生,但是在這個(gè)例子中沒有出現(xiàn)錯(cuò)誤。

步驟6:構(gòu)建與運(yùn)行

再一次運(yùn)行你的程序,它將正確的顯示您已經(jīng)使用了多少空間。

6 總結(jié)

我們來回顧一下組件的生命周期,當(dāng)我們打開今日面板,系統(tǒng)可能會(huì)顯示上次的快照知道準(zhǔn)備完成。界面會(huì)被加載,組件將取出緩存在NSUserDefaults的來更新用戶界面。

接著widgetPerformUpdateWithCompletionHandler:會(huì)被調(diào)用,重新計(jì)算真實(shí)的值,如果緩存的值和真實(shí)值相近,不會(huì)發(fā)生任何事情,如果存在不同,那么新的值會(huì)被緩存界面會(huì)被刷新。

在后臺(tái)的時(shí)候,組件可能會(huì)被系統(tǒng)調(diào)用,如果返回NCUpdateResultNewData,新的快照就會(huì)被創(chuàng)建為了下次的出現(xiàn)。

7添加更多得信息和動(dòng)畫

雖然我們已經(jīng)完成了顯示使用空間的功能,它會(huì)有一個(gè)精確的數(shù)字。為了避免復(fù)雜的用戶界面,使交互更加友好。如果點(diǎn)擊百分比標(biāo)簽,組件將額外顯示一個(gè)新的標(biāo)簽。這也是一個(gè)很好的機(jī)會(huì)去學(xué)習(xí)如何在組件中使用動(dòng)畫。

步驟1:改變用戶界面

打開MainInterface.storyboard選擇label,在In the Attributes Inspector,在View選項(xiàng)下,找到User Interaction Enabled選項(xiàng)并啟用它。

接下來,我們需要去除label的底部約束。label視圖的底部的距離會(huì)改變,這意味著約束將成為無效。

選擇label,在Size Inspector展開'Size',選擇底部的空間約束,并點(diǎn)擊刪除。你也可以手動(dòng)選擇視圖中的約束引導(dǎo)并刪除它。label現(xiàn)在只有頂部的約束,如下圖所示。

通過點(diǎn)擊畫面上方的三個(gè)圖標(biāo)的第一個(gè)選擇視圖控制器。在Size Inspector的Size中,將高度設(shè)置為106。

添加一個(gè)新的label,,在Attributes Inspector設(shè)置其顏色為白色。此外,設(shè)置行數(shù)3,高度61,寬度200。這應(yīng)該足以容納三條信息。讓它保存靠左和靠下對(duì)齊。

最后一步是打開助理編輯和創(chuàng)建名為detailslabel的outlet。

步驟2:安裝

控件將只會(huì)在一瞬間擴(kuò)大。我們可以在NSUserDefaults保存一個(gè)布爾變量來記住上一次的狀態(tài),但是,為了簡(jiǎn)單,每次組件加載它時(shí)將關(guān)閉。當(dāng)點(diǎn)擊百分比label的時(shí)候,額外的信息將會(huì)出現(xiàn)。

讓我們首先定義兩個(gè)宏在TodayViewController.m來幫助我們?cè)O(shè)置大小。 在viewDidLoad,添加兩行代碼來設(shè)置控件的初始高度,讓label透明。我們將在百分比label被點(diǎn)擊的時(shí)候使詳細(xì)label出現(xiàn)。

請(qǐng)注意,我們?cè)O(shè)置控件的寬度為0,因?yàn)閷挾葘⒂刹僮飨到y(tǒng)決定。

更新詳情標(biāo)簽

在詳情label中,我們使用NSByteCountFormatter來展示可用、已使用和總的值。添加以下代碼的視圖控制器。

-(void)updateDetailsLabel

{

NSByteCountFormatter *formatter =

[[NSByteCountFormatter alloc] init];

[formatter setCountStyle:NSByteCountFormatterCountStyleFile];

self.detailsLabel.text =

[NSString stringWithFormat:

@"Used:\t%@\nFree:\t%@\nTotal:\t%@",

[formatter stringFromByteCount:self.usedSize],

[formatter stringFromByteCount:self.freeSize],

[formatter stringFromByteCount:self.fileSystemSize]];

}

步驟4:檢測(cè)點(diǎn)擊事件

為了檢測(cè)點(diǎn)擊事件,我們?cè)谠噲D中重寫了touchesBegan:withEvent:方法。想法很簡(jiǎn)單,當(dāng)檢測(cè)到觸摸事件,展開詳情標(biāo)簽并更新。請(qǐng)注意,插件的大小的是在callingsetPreferredContentSize更新的。

步驟5:添加動(dòng)畫

雖然這個(gè)組件運(yùn)行的不錯(cuò),我們還是可以通過在收展詳情標(biāo)簽使用漸變效果來提升用戶體驗(yàn),這個(gè)可以通過實(shí)現(xiàn)viewWillTransitionToSize:withTransitionCoordinator:來完成。這個(gè)方法會(huì)在組件的高度變化的時(shí)候被調(diào)用。因?yàn)檫@是通過漸變協(xié)調(diào)對(duì)象來完成,所以我們可以添加一些額外的動(dòng)畫。

你可以看到的,我們改變了標(biāo)簽的alpha值,但是你可以添加任何類型的動(dòng)畫來增強(qiáng)的用戶體驗(yàn)。

步驟6:構(gòu)建與運(yùn)行

我們?cè)僖淮芜\(yùn)行程序。通過點(diǎn)擊百分比label來現(xiàn)實(shí)詳情label。

結(jié)論

這些邏輯似乎過于復(fù)雜對(duì)于這樣一個(gè)簡(jiǎn)單的任務(wù),但是你將熟悉創(chuàng)建今日擴(kuò)展的的全過程。記住這些原則在設(shè)計(jì)和開發(fā)你的插件的時(shí)候。記得要保持它的簡(jiǎn)單和明了,同時(shí)也別忘了性能。

緩存在一些快速操作也許不需要,但你要做耗時(shí)的處理它就變得尤為重要。用你的視圖控制器的知識(shí)來檢查它是否能夠在各種屏幕尺寸下工作。建議你避免滾動(dòng)視圖或復(fù)雜的觸摸識(shí)別。

雖然擴(kuò)展將有一個(gè)單獨(dú)的容器,正如我們前面看到的,它可以使應(yīng)用程序和包含擴(kuò)展之間的數(shù)據(jù)共享。你也可以使用NSExtensionContext的OpenURL:completionhandler:通過一個(gè)自定義的URL scheme來快速啟動(dòng)你的程序從組件。如果你需要分享你的延伸,創(chuàng)建一個(gè)框架來使用您的應(yīng)用程序和擴(kuò)展。

我希望這里介紹的知識(shí)是有用的,幫助創(chuàng)建你的下一個(gè)偉大的今天擴(kuò)展。

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評(píng)論 19 139
  • 前段時(shí)間因?yàn)轫?xiàng)目需要,接觸了iOS Today Extension 的開發(fā),網(wǎng)絡(luò)上關(guān)于完整的 Today Exte...
    十一點(diǎn)就要睡覺的碼農(nóng)閱讀 7,414評(píng)論 18 14
  • PS:此文僅作對(duì)TodayExtension的一些簡(jiǎn)單的使用,且大多參看前輩文章所寫,作者目前水平尚水…. 學(xué)無止...
    wizet閱讀 1,831評(píng)論 1 3
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,881評(píng)論 25 709
  • 文/呢喃 給逸陽起名字的時(shí)候,是有幾層意思在里頭的,逸,取其超凡脫俗,卓爾不群之意,也希望他以后的生活安閑,安樂;...
    米粒說閱讀 851評(píng)論 5 3

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