iOS - 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里面的数据存取。