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裡面的資料存取。