從0到1思考與實(shí)現(xiàn)iOS-Widget

講述之前首先看下demo效果圖:

基本的展開收起、本App本體交互

然后再展示幾個(gè)效果不錯(cuò)的 Widget app


毒物 && Keep
ESPN
PCalc
Musixmatch
Fantastical 2
Carrot Weather

demo 地址在此熙宇!歡迎star

比心

一、Widget總覽

  • Widget 是 iOS8 推出第一版坎匿,在iOS 10 進(jìn)行大幅度的優(yōu)化
  • Widget可以讓用戶更快地訪問到其感興趣的內(nèi)容绢淀,官方的說法是用來呈現(xiàn)功能比較簡(jiǎn)單的,交互性不強(qiáng)的東西丢早,在不打擾或者中斷用戶使用當(dāng)前應(yīng)用的前提下完成自己的功能點(diǎn).對(duì)于這個(gè)說法姨裸,國(guó)內(nèi)的開發(fā)者表示呵呵秧倾,因?yàn)閹缀跛械?Widget都綁定了對(duì)應(yīng)的點(diǎn)擊事件

二、Widget代碼實(shí)現(xiàn)

  • 因?yàn)?Widget 屬于單獨(dú)的進(jìn)程傀缩,因此需要再新建一個(gè)target:File -> New ->target


  • 初次構(gòu)建 UI 時(shí)那先,運(yùn)行 Widget 后會(huì)發(fā)現(xiàn),Widget左側(cè)距離屏幕左側(cè)始終有一段距離赡艰,導(dǎo)致效果不佳售淡,可以通過下面的代理方法消除間距

// 取消widget默認(rèn)的inset,讓應(yīng)用靠左
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets {
    return UIEdgeInsetsZero;
}
  • Widget 的收起慷垮、展開 則是通過這個(gè)代理方法:
/**
 activeDisplayMode有以下兩種
     NCWidgetDisplayModeCompact, // 收起模式
     NCWidgetDisplayModeExpanded, // 展開模式
 */
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
    if(activeDisplayMode == NCWidgetDisplayModeCompact) {
        // 尺寸只設(shè)置高度即可揖闸,因?yàn)閷挾仁枪潭ǖ模O(shè)置了也不會(huì)有效果
        self.preferredContentSize = CGSizeMake(0, 110);
    } else {
        self.preferredContentSize = CGSizeMake(0, 310);
    }
}
  • 在設(shè)置 UI 的過程中料身,若想使用本體 Target 中的類:


    在對(duì)應(yīng)類的 Target Membership 勾選 Widget 即可
  • 如果想使用Pod 管理的第三方庫汤纸,那么只需要以下三步就可以愉快地玩耍了(比如我想使用 Masonry 布局)
    1、 在podfile文件中



    2芹血、 按照如圖所示配置configurations



    3贮泞、 最后分別配置兩個(gè) Target 的 link Binanry

當(dāng)然有些第三方包含 source 文件的可能還需要?jiǎng)e的操作楞慈,最簡(jiǎn)單粗暴的方式就是-->拖進(jìn)去!

  • 使用圖片也是必不可少啃擦,然而 imageNamed: 和 imageWithContentsOfFile: 兩種方式加載都不行囊蓝,即使設(shè)置了文件的 target 為 Widget Extension,后來在其target 內(nèi)部建立一個(gè) .xcassets 文件即可加載圖片


  • 然而在 Widget Extension 里面新建類又出現(xiàn)了如下報(bào)錯(cuò)


    • 造成這個(gè)的原因是新建的時(shí)候默認(rèn)是 C header令蛉,而且沒有指向?qū)?yīng)的target聚霜,按照下圖所示修改一下type,選一下target言询,再次編譯就木有問題了
  • 如果需要網(wǎng)絡(luò)請(qǐng)求俯萎,記住在 Extension 的plist文件中添加App Transport Security Settings 屬性
  • 在開發(fā)過程中,那么怎么一直有個(gè)“Hello World”顯示运杭,最后看了一下原來是 Storyboard 加載夫啊,去 Storyboard 文件刪除對(duì)應(yīng) label 即可
  • 如果你的項(xiàng)目中要求純代碼
    • 刪除 Storyboard 文件和plist 對(duì)應(yīng)鍵值對(duì)
    • 添加 NSExtensionPrincipalClass 字段并設(shè)置為 TodayViewController



三、與 App 本體交互

與本體 app 進(jìn)行交互之前辆憔,要明白的一個(gè)概念是:Widget 與 app 本身 是兩個(gè)target撇眯,appId 也是獨(dú)立的,因此 Widget 與本體 app 是通過 app group 進(jìn)行交互

1虱咧、設(shè)置群組關(guān)系

在 本體 App 的 target > Capabilities添加 container 標(biāo)識(shí)符

這個(gè)寫好之后熊榛,再去擴(kuò)展的target做相同的操作,標(biāo)識(shí)符一定要一樣M笱病玄坦!
切換 target 的方法在這里
  • 報(bào)錯(cuò)信息:[_NCWidgetExtensionContext openURL:completionHandler:]_block_invoke failed: Error Domain=NSOSStatusErrorDomain Code=-50 "(null) 如果報(bào)這個(gè)錯(cuò)說明 urlScheme有問題,沒有標(biāo)準(zhǔn)對(duì)應(yīng)绘沉,比如下劃線識(shí)別等
2煎楣、設(shè)置 scheme 進(jìn)行交互
  • 設(shè)置 app 的 scheme 標(biāo)識(shí)符


    在plist 文件內(nèi)添加以下鍵值對(duì)
  • 然后!就可以在 Widget 對(duì)應(yīng)的點(diǎn)擊事件里面

// 掃一掃按鈕的點(diǎn)擊事件
- (void)scanBtnTapped:(UIButton *)sender {
    [self.extensionContext openURL:[NSURL URLWithString:@"wpfWidgetTest://action=richScan"] completionHandler:^(BOOL success) {
        NSLog(@"scanBtnTapped   open url result:%d",success);
    }];
}
  • 在 app 本體的 AppDelegate 方法里面
// 處理 Widget 相關(guān)事件
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
    
    NSString* prefix = @"wpfWidgetTest://action=";
    NSString *urlString = [url absoluteString];
    
    if ([urlString rangeOfString:prefix].location != NSNotFound) {
        NSString *action = [urlString substringFromIndex:prefix.length];
        if ([action isEqualToString:@"richScan"]) {
            // 進(jìn)入到掃一掃頁面
            [self.rootVC transferToRichScanVC];
        } else if ([action isEqualToString:@"web"]) {
            // 進(jìn)入到 web 活動(dòng)頁
            [self.rootVC transferToWebVCWithUrlString:@"webTest"];
        } 
    }
    return  YES;
}
  • 數(shù)據(jù)共享:widget項(xiàng)目必然經(jīng)常要和主項(xiàng)目共享數(shù)據(jù)车伞,可以通過NSUserDefault择懂,注意和平時(shí)用有些不同,創(chuàng)建UserDefault的時(shí)候另玖,要指定groupid困曙。上代碼:
// widget項(xiàng)目里取數(shù)據(jù)
+ (NSString*)widgetStringForKey:(NSString*)defaultName {
     NSUserDefaults*shared = [[NSUserDefaultsalloc] initWithSuiteName:@"group.com.widgetTest"];
     return[shared stringForKey:defaultName];
}

// 主項(xiàng)目里存數(shù)據(jù)
+ (void)widgetSetObject:(id)value forKey:(NSString*)defaultName {
    NSUserDefaults*shared = [[NSUserDefaultsalloc] initWithSuiteName:@"group.com.widgetTest"];
    [shared setObject:value forKey:defaultName];
    [shared synchronize];
}

#warning 涉及到大量數(shù)據(jù)交互也可以使用 NSFileManager 進(jìn)行數(shù)據(jù)共享

在demo中,實(shí)現(xiàn)了從Widget入口 點(diǎn)擊未讀消息后谦去,下次不再展示該未讀消息項(xiàng)



四慷丽、關(guān)于刷新時(shí)機(jī)

  • Widget 自身的更新機(jī)制,是進(jìn)入到 Widget 頁面后(iOS 10 左滑鳄哭,之前是下拉)要糊,先執(zhí)行 viewDidLoad 方法,然后是 viewWillAppear 方法窃诉,但是經(jīng)測(cè)驗(yàn)杨耙,Widget 頁面在屏幕消失超過兩秒后(手機(jī)沒有停留在 Widget 頁面 或者 停留在別的app 的Widget頁面赤套,自己的沒顯示)
  • 由于以上特性,更新代碼最好寫在 viewWillAppear 方法里面珊膜,對(duì)于更新時(shí)效性特別強(qiáng)的容握,比如天氣類 app,這種最好就是 在該方法里面添加一個(gè) NSTimer 定時(shí)進(jìn)行刷新车柠,在 viewWillDisAppear 方法中 進(jìn)行 取消NSTimer invalidate定時(shí)更新即可
  • 知乎剔氏、得到 app的 Widget,只要走 viewDidLoad 方法就會(huì)閃一下(如下圖)竹祷,因?yàn)槊看蜽idget加載請(qǐng)求的數(shù)據(jù)后會(huì)進(jìn)行替換造成的谈跛。這里可以做個(gè)緩存優(yōu)化,判斷如果請(qǐng)求來的數(shù)據(jù)和當(dāng)前數(shù)據(jù)內(nèi)容一致塑陵,那么就不進(jìn)行刷新列表操作
    不信你看

五感憾、關(guān)于 iOS8 適配

  • iOS8、9是老式的下拉刷新令花,并沒有折疊和展開功能阻桅,默認(rèn)的Widget高度為self.preferredContentSize設(shè)置的高度
  • iOS8 默認(rèn)的背景是黑色磨砂效果,iOS10默認(rèn)的背景色是白色磨砂效果兼都。因此在控件顏色上做下適配
iOS8效果圖
  • iOS8下所有組件默認(rèn)右移30pt

六嫂沉、其他注意點(diǎn)

  1. 當(dāng)程序內(nèi)存不足時(shí),蘋果優(yōu)先會(huì)殺死擴(kuò)展扮碧,因此需要注意內(nèi)存的管理趟章。

  2. 在配置team是賬號(hào)需要一致(免費(fèi)賬號(hào)不行,需要付費(fèi)的賬號(hào))慎王,上傳包的時(shí)候一定注意選擇 Product -> Archive -> ** 選擇 distribution 模式蚓土!**

  3. 3D touch 對(duì)應(yīng)的也有Widget!柬祠?答案是 YES北戏!负芋,只要設(shè)置了3D touch漫蛔,Widget的第一欄就會(huì)自動(dòng)顯示。但是如果有多個(gè)widget的話旧蛾,還需要在 info.plist 指定相應(yīng)的main target莽龟!

Extension 證書配置指南
官網(wǎng)說明
一直很心儀的app --> Things 關(guān)于widget的介紹
幾個(gè)精致的 Widget app
在模擬器上進(jìn)行3D touch 測(cè)試


再次附上 demo Github 地址,歡迎star

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锨天,一起剝皮案震驚了整個(gè)濱河市毯盈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌病袄,老刑警劉巖搂赋,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赘阀,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡脑奠,警方通過查閱死者的電腦和手機(jī)基公,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宋欺,“玉大人轰豆,你說我怎么就攤上這事〕莸” “怎么了酸休?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)祷杈。 經(jīng)常有香客問我斑司,道長(zhǎng),這世上最難降的妖魔是什么但汞? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任陡厘,我火速辦了婚禮,結(jié)果婚禮上特占,老公的妹妹穿的比我還像新娘糙置。我一直安慰自己,他們只是感情好是目,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布谤饭。 她就那樣靜靜地躺著,像睡著了一般懊纳。 火紅的嫁衣襯著肌膚如雪揉抵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天嗤疯,我揣著相機(jī)與錄音冤今,去河邊找鬼。 笑死茂缚,一個(gè)胖子當(dāng)著我的面吹牛戏罢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脚囊,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼龟糕,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了悔耘?” 一聲冷哼從身側(cè)響起讲岁,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后缓艳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體校摩,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年阶淘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秧耗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡舶治,死狀恐怖分井,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情霉猛,我是刑警寧澤尺锚,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站惜浅,受9級(jí)特大地震影響瘫辩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜坛悉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一伐厌、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧裸影,春花似錦挣轨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至均践,卻和暖如春晤锹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背彤委。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工鞭铆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人焦影。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓车遂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親偷办。 傳聞我的和親對(duì)象是個(gè)殘疾皇子艰额,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

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