iOS開發(fā) Widget(小組件)的開發(fā)

在iOS開發(fā)中,Widget(小組件)的功能屬于雞肋的功能,因為大多數(shù)的人都很少去使用,或者很多人都不知道還有這功能。不過在項目完成時可以添加上去,增加項目的bigger。本文會詳細介紹小組件的開發(fā)以及開發(fā)中會遇到的坑,如果大家又遇到新的坑,可以在下面留言,大家一起交流。
聲明:本文是使用Xcode11.0beta6版本+iOS13.1beta2版本作為展示用例。但是項目創(chuàng)建是使用Xcode10.3創(chuàng)建的,這是因為害怕部分用戶下載Demo后無法在Xcode10中運行。現(xiàn)在使用Xcode的11創(chuàng)建APP會多一個SceneDelegate,在Xcode10中無法識別,也會簡單的說明一下當前Demo找Xcode10與Xcode11的簡單區(qū)別。所展示的支付寶版本為10.1.72,今日頭條版本為7.4.0。(避免今后兩款A(yù)PP更新以后與本文展示的不一致)

1.什么是小組件

小組件即如圖的下面兩種呈現(xiàn)方式:第一種是在最左頁(俗稱第0頁)或者是往下滑的時候會出現(xiàn)的頁面,展示如下:


最左頁

這個需要用戶去點擊編輯才會顯示出來。
第二種展示方式是長按當前APP后會出來(也就是3DTouch),展示如下:


長按顯示1
長按顯示2

這種方式只要長按就會觸發(fā),不需要用戶去添加。
常見的小組件都是這兩種方式顯示,一種是支付寶的那種固定樣式的,點擊某一個功能后就跳轉(zhuǎn)到APP的某個頁面去。另外一種是今日頭條的動態(tài)顯示的,每次都回去請求數(shù)據(jù)然后刷新顯示。接下來兩種方式的開發(fā)都會進行講解。

2.靜態(tài)小組件的開發(fā)(類似支付寶的樣式)

對于想做成類似支付寶的點擊小組件的某個按鈕就跳轉(zhuǎn)的指定頁面的是最簡單的。有一些其他文章上來就說需要去添加APP Groups的對于這種類型的來說是不必要的,這個不涉及到數(shù)據(jù)共享的東西,就簡單的跳轉(zhuǎn),完全沒必要去進行創(chuàng)建APP Groups。下面說明詳細步驟:
完成主項目后,點擊如下兩個步驟完成widget的創(chuàng)建:


添加方式1.png
添加方式2.png

選擇Today Extension:


Today Extension

填寫項目名稱即可完成創(chuàng)建。接下來在TodayViewController的viewDidLoad里面進行按鈕的創(chuàng)建:

    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    //NCWidgetDisplayModeCompact   不需要折疊樣式,整個完整顯示
    //NCWidgetDisplayModeExpanded  有折疊按鈕,點擊可以顯示更多
    self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeCompact;
    //設(shè)置整個小組件的寬高
    self.preferredContentSize = CGSizeMake(width, 130);
    NSArray *titleArray = @[
                            @"掃一掃",
                            @"掃一掃",
                            @"掃一掃",
                            @"掃一掃",
                            ];
    NSArray *imageArray = @[
                            @"widget_scan",
                            @"widget_scan",
                            @"widget_scan",
                            @"widget_scan",
                            ];
    CGFloat btnWidth = 60;
    CGFloat btnHeight = 60;
    CGFloat margin = (width - titleArray.count * btnWidth) / (titleArray.count + 1);
    CGFloat y = 20;
    for (NSInteger i = 0; i < titleArray.count; i ++) {
        CGFloat x = margin + i * (btnWidth + margin);
        UIButton *btn = [[UIButton alloc] init];
        btn.frame = CGRectMake(x, y, btnWidth, btnHeight);
        [btn setImage:[UIImage imageNamed:imageArray[i]] forState:UIControlStateNormal];
        [btn setTitle:titleArray[i] forState:UIControlStateNormal];
        btn.tag = i;
        [btn addTarget:self action:@selector(btnDidClick:) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:btn];
    }

運行后效果如下圖:
運行效果圖

這是為什么呢?沒有寫hello world確顯示出來了。這是因為系統(tǒng)默認是用小組件的MainInterface.storyboard進行顯示的,如果你喜歡使用storyboard直接在里面布局就行了,但是我一般都用代碼開發(fā),所以需要去當前小組件的文件下的info.plist刪除 NSExtensionMainStoryboard 選項,增加 NSExtensionPrincipalClass,value為類的名字TodayViewController即可用純代碼進行布局。
配置純代碼.png

配置完成后再次運行。
運行結(jié)果

現(xiàn)在能看到文字顯示出來了,但是圖片沒有,這是因為在放進去圖片的時候,沒有勾選在widget項目里面也能共,所以當前項目找不到圖片,只需要去勾選就可以在當前文件使用即可。
圖片添加

順便說一下,主項目里面的文件如何讓widget也能使用,如果是單純的.h文件,比如之前存放的一些宏的定義,顏色的定義之類的,只需要在這個地方設(shè)置導入即可:
引入頭文件

如果想引入主項目里面的其他文件,也和圖片一樣的勾選,只是在上面那里導入是不行的,必須進行勾選才能使用,比如我需要導入一個分類文件:


分類文件導入

勾選圖片以后就能看到圖片了。
demo有不完美的地方,按鈕的圖片和title不能對齊,不過不影響demo的演示:)

布局完成能顯示以后,接下來進行點擊按鈕跳轉(zhuǎn),由于需要跳轉(zhuǎn)到主項目的某個頁面,所以需要在主項目中用到URL Types。強烈建議設(shè)置成包名的形式,否則和其他APP一樣的schemes可能會跳轉(zhuǎn)他們的APP去
URL Types

ok,配置完成后,只需要在widget的按鈕里面實現(xiàn)點擊事件然后在主項目的AppDelegate的openURL里面實現(xiàn)點擊以后的判讀即可。
widget的點擊事件如下:
- (void)btnDidClick:(UIButton *)btn{
    NSInteger index = btn.tag;
    //因為我是需要跳轉(zhuǎn)到某個頁面,所以我直接傳遞跳轉(zhuǎn)頁面的類名
    NSString *vcString = @"";
    if (index == 0) {
        vcString = @"OneViewController";
    }else if (index == 1){
        vcString = @"OneViewController";
    }else if (index == 2){
        vcString = @"ViewController";
    }else if (index == 3){
        vcString = @"TwoViewController";
    }
    [self.extensionContext openURL:[NSURL URLWithString:[NSString stringWithFormat:@"com.widgetDemo://%@",vcString]] completionHandler:nil];
}

AppDelegate的openURL如下:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{
    [self appCallbackWithOpenUrl:url];
    return YES;
}

- (void)appCallbackWithOpenUrl:(NSURL *)url{
    // 針對url進行不同的操作
    NSString *URLString = [NSString stringWithFormat:@"%@",url];
    NSArray *array = [URLString componentsSeparatedByString:@"://"];
    NSString *vcString = @"";
    //這里最好進行判斷URL的開頭是不是設(shè)置的schemes,如果用到其他的第三方SDK,有可能是他們的URL
    if (array.count >= 2) {
        vcString = array[1];
    }
   //這里可以先判斷是否登錄或者其他邏輯在進行下面的操作
        
    if ([vcString isEqualToString:@"OneViewController"]) {
        UINavigationController *nav = ((UITabBarController*)self.window.rootViewController).selectedViewController;
        UIViewController *VC = [[NSClassFromString(vcString) alloc] init];
        [nav pushViewController:VC animated:YES];
    }else if ([vcString isEqualToString:@"ViewController"]){
        UINavigationController *nav = ((UITabBarController*)self.window.rootViewController).selectedViewController;
        UIViewController *VC = [[NSClassFromString(vcString) alloc] init];
        [nav pushViewController:VC animated:YES];
    }else if ([vcString isEqualToString:@"TwoViewController"]){
        UITabBarController *tabBarVC = (UITabBarController *)self.window.rootViewController;
        tabBarVC.selectedIndex = 1;
    }
}

一個固定點擊跳轉(zhuǎn)的小組件就這樣開發(fā)完成,很簡單的。如果不需要數(shù)據(jù)共享,根本不需要其他文章說的先去配置APP Groups,就如此簡單即可。

3.動態(tài)小組件的開發(fā)(類似今日頭條的樣式)

做了靜態(tài)的小組件以后,動態(tài)的也不難,類似于今日頭條的小組件,會有數(shù)據(jù)的共享,以及數(shù)據(jù)的請求。但是有以下注意點:

3.1主項目框架的使用

如果項目中用CocoaPods導入了第三方框架,比如Masonry和AFNetworking。在小組件的項目中有可能是能直接導入頭文件的,但是實際使用卻是不行的。這時候需要在podfile文件中加入小組件的target然后在pod install,這樣在小組件的項目中才能使用。
pod文件

這樣以后才能在小組件的項目里面使用第三方框架。

3.2數(shù)據(jù)共享

如果需要將主APP的某些數(shù)據(jù)共享給小組件使用,這時候就需要進行數(shù)據(jù)共享,使用到APP Groups,本次以Xcoede11截圖。在Xcode10中,直接把開關(guān)打開添加即可。因為我直接在Xcode里面登錄了賬號,所以不需要去網(wǎng)頁上進行證書那些的配置即可,如果是用其他方式的,需要去網(wǎng)頁上進行相關(guān)的設(shè)置。
APP Groups

然后就可以在需要存儲的地方進行數(shù)據(jù)的存儲了,不過現(xiàn)在只支持兩種存儲,一個是NSUserDefaults,一個是NSFileManager。
NSUserDefaults存儲數(shù)據(jù)

NSUserDefaults *sharedData = [[NSUserDefaults alloc] initWithSuiteName:GroupID];
    [sharedData setValue:@"測試存儲" forKey:Name];
    [sharedData synchronize];

NSFileManager 存儲數(shù)據(jù)

NSError *error = nil;
    NSURL *containUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:GroupID];
   containUrl = [containUrl URLByAppendingPathComponent:Data];
    NSString *text = @"NSFileManager 存儲數(shù)據(jù)";
    BOOL result = [text writeToURL:containUrl atomically:YES encoding:NSUTF8StringEncoding error:&error];
    if (result){
        NSLog(@"save success");
    }else {
        NSLog(@"error:%@", error);
    }

在這個方法你可以獲取到時候折疊

// 展開/折疊監(jiān)聽
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize{
    
}

至此,兩種方式創(chuàng)建的小組件都完成。
如果大家還遇到什么其他問題可以在下面留言,大家一起交流交流。
最后附上Demo地址
)

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

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

  • ——最美不過你曾溫柔呼喚,而我剛好有過應(yīng)答 我希望你能被打磨 永遠光明磊落 我...
    旺角煙頭閱讀 387評論 0 4
  • 今天媽媽陪我去公園玩,看到了幾只黑天鵝,媽媽說黑天鵝近乎已經(jīng)絕跡,讓我們保護動物,愛護動物。今天玩的很開心,也學到...
    5d137d4a8501閱讀 166評論 0 0
  • 從課時量可以看出來,目前語文課絕大部分課時實際上花在閱讀教學,上寫作課是很少的。布置作業(yè)的時候呢,有隨筆日記等形式...
    小姜老師0301閱讀 409評論 0 4
  • 荷香滿塘的七月,和朋友在陸河吃完晚飯,便開車經(jīng)螺溪向上砂的方向出發(fā)。 千山夜月明,夏涼滿山坳。把車里空...
    懵園園閱讀 1,067評論 0 3

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