1. 程式人生 > >Widget小部件(Today Extension)

Widget小部件(Today Extension)

Today Extension是iOS8新開放的一種對幾個固定系統區域的擴充套件機制,它可以在一定程度上彌補iOS的沙盒機制對應用間通訊的限制,iOS10之後又添加了一些功能。可能好多人不知道或不怎麼用Widget。那到底什麼小部件呢?看圖:
這裡寫圖片描述
看到這個圖你應該就知道Widget這是什麼了吧,可能你正在使用它。

之後我們研究下面幾個問題:

1、為什麼要使用widget;
2、如何在現有的工程新增widget;
3、如何試圖佈局;
4、如何調起主App以及進入相應的頁面;
5、如何與主App共享資料。

為什麼要使用widget呢?

它為我們的應用提供了一種便捷的服務方式,比如使用者可以在Today Extension中檢視應用展示的簡略資訊,而不用再進到我們的應用中,同樣可以快捷操作app的功能,這將是一種全新的使用者體驗。重點就是便捷、快,這是蘋果在iOS12中所追求的。

特點:Widget你可以看作是一個獨立的小專案,當你新增好之後,你會發現它和主App不互通,想要很好的應用它,需要配置一些檔案。如下面這些步驟:

如何在現有的工程新增widget

首先開啟你的專案,在原有的工程基礎上,想要使用Today Extension,我們需要建立一個新的target,點選File–>New–>Target–>Today Extention,如下圖所示:
這裡寫圖片描述
這裡寫圖片描述
新增成功後項目的目錄會如下圖所示:
這裡寫圖片描述
執行專案會看到如下圖所示的效果:
這裡寫圖片描述
注意:這裡你執行的專案時會發現有兩個可執行專案(Widget是獨立的),當你執行主專案時,是無法除錯Widget的,無法打斷點除錯,無法列印一些資料等。

如何試圖佈局

試圖佈局這裡分兩種,純程式碼和storyboard,如果想用純程式碼,需要在plist檔案裡配置一點東西:選擇刪除預設建立的MainInterface.storyboard,並在info.plist中刪除NSExtensionMainStoryboard,新增NSExtensionPrincipalClass為TodayViewController。
這裡寫圖片描述
設定檢視的大小:

- (void)viewDidLoad {
    [super viewDidLoad];
    // iOS10,設定展開摺疊
    self.extensionContext.widgetLargestAvailableDisplayMode
= NCWidgetDisplayModeExpanded; }
#pragma mark - NCWidgetProviding
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize NS_AVAILABLE_IOS(10_0){
    if (activeDisplayMode == NCWidgetDisplayModeCompact){
        // 摺疊後預設高度110
        self.preferredContentSize = CGSizeMake(SCREEN_WIDTH, 110);
    }else{
        // 展開後的高度由你來定
        self.preferredContentSize = CGSizeMake(SCREEN_WIDTH, 485);
    }
}

注意:這裡如果你想用masonry庫來做約束,主App裡Cocoapods匯入的masonry是用不了的,你可以把masonry再匯入你的 CNTodayViewController 裡面,因為它是一個獨立的專案,所以好多東西都需要你重新匯入,但是Widget講的是便捷,不會有太複雜的佈局,一般就幾個按鈕,調起主App頁面用的,像QQ音樂就三個按鈕,很簡潔。

如何調起主App以及進入相應的頁面

Today Extension只能通過openURL的方式來調起app,並且需要在info.plist檔案中配置引數URL types。

主App工程中配置:
這裡寫圖片描述
Today Extension配置:
這裡寫圖片描述
URL identifier為app的bundle ID,URL Schemes配置為app的scheme

#pragma mark - Widget 通過openURL的方式啟動Containing APP
- (void)openURLContainingAPP{
    //scheme為app的scheme
    [self.extensionContext openURL:[NSURL URLWithString:@"CNCrm://"]
                 completionHandler:^(BOOL success) {
                     NSLog(@"open url result:%d",success);
                 }];
}
#pragma mark - Widget 進入主App不同的頁面
- (void)openDiffirentPage{
[self.extensionContext openURL:[NSURL URLWithString:@"CNCrm://Action=HomePage"]
                     completionHandler:^(BOOL success) {
                         NSLog(@"open url result:%d",success);
                     }];
[self.extensionContext openURL:[NSURL URLWithString:@"CNCrm://Action=DetailPage"]
                     completionHandler:^(BOOL success) {
                         NSLog(@"open url result:%d",success);
                     }];
[self.extensionContext openURL:[NSURL URLWithString:@"CNCrm://Action=MyPage"]
                     completionHandler:^(BOOL success) {
                         NSLog(@"open url result:%d",success);
                     }];
 }

在主App的AppDelegate.m裡面:

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options NS_AVAILABLE_IOS(9_0){
     NSString* prefix = @"CNCrm://Action=";
        if ([[url absoluteString] rangeOfString:prefix].location!=NSNotFound) {
            NSString *action = [[url absoluteString] substringFromIndex:prefix.length];
            if ([action isEqualToString:@"HomePage"]) {
            // 自行處理事件
            } else if ([action isEqualToString:@"DetailPage"]){
            // 自行處理事件
            } else if ([action isEqualToString:@"MyPage"]){
            // 自行處理事件
            }
        }
    return YES;
}

如何與主App共享資料

首先需要去蘋果開發者中心的APP Groups中建立一個APP Group,命名方式”group.com.companyName.xxx”,如下圖:
這裡寫圖片描述
建立好Group ID之後,回到專案裡面配置(主App和Today Extension都要配置):

主App和Today Extension的配置都如下圖所示:
這裡寫圖片描述

通過App Groups提供的同一group內app共同讀寫區域,可以用NSUserDefaults和NSFileManager兩種方式實現Today Extension和containing app之間的資料共享。

通過NSUserDefaults共享資料

- (void)saveDataByNSUserDefaults
{
    NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.xxx.xxx"];
    [shared setObject:@"哈哈" forKey:@"Widget"];
    [shared synchronize];
}
- (NSString *)readDataFromNSUserDefaults
{
    NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.xxx.xxx"];
    NSString *value = [shared valueForKey:@"Widget"];

    return value;
}

通過NSFileManager共享資料

- (BOOL)saveDataByNSFileManager
{
    NSError *error = nil;
    NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.xxx.xxx"];
    containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];

    NSString *value = @"test";
    BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&error];
    if (!result) {
        NSLog(@"%@",error);
    } else {
        NSLog(@"save value:%@ success.",value);
    }

    return result;
}
- (NSString *)readDataByNSFileManager
{
    NSError *error = nil;
    NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.xxx.xxx"];
    containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];
    NSString *value = [NSString stringWithContentsOfURL:containerURL encoding:NSUTF8StringEncoding error:&error];

    return value;
}

通過這兩個方法就可以實現了Today Extension與主App的資料共享,類似於主App裡面的資料存取。