前言
对于一款应用来说,想要获得更多关注,想要更频繁的出现在用户的视野中,推送、widget都是必不可少的,推送在实际开发中,是很常见的,那我们就来玩一玩不常见的widget吧!
创建一个TodayExtension
新创建一个项目,创建完成后,再在项目中新加入一个target。target选Today Extension。
新建target
选择todayExtension
当然TodayExtension作为一个单独的target,也是需要证书的,为了demo方便,我们就直接自动管理了。
需要证书
创建成功后,你会发现,新的target里面多了一个controller,这个controller就是我们所能看到的widget,而且他继承自UIViewController,所以我们可以正常的像开发其他功能一样来做widegt,不过widget是有内存限制的,请尽量做些简单的功能。
观察iOS10的widget你会发现,他有两种状态,分别是折叠、展开。
展开折叠
我们在TodayViewController加入折叠的支持。首先在viewWillAppear中加入折叠的代码。当然折叠特性是iOS10新加入的,需要做下版本区分,防止非iOS10设备崩溃。
- (void)viewWillAppear:(BOOL)animated {
// 设置widget的状态为展开
self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
然后有个协议方法需要实现,协议为:
协议
方法为:
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
if (activeDisplayMode == NCWidgetDisplayModeCompact) {
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 110);
} else {
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 300);
}
}
该方法,指定折叠和展开两个状态下的widget大小。现在再运行,你就可以正常添加我们制作的widget到通知中心了。
调起主程序
通过上面的步骤,你就可以正常的创建任何你想做的widget插件了。但我们最关心的不是widget该怎么做,因为它作为一个UIViewController,以我们的开发经验想创造出各种页面,都是没问题的。
我们最关心的是,如何通过widget,将用户引导到我们的APP中,也就是应用跳转。以支付宝为例,对支付宝进行3D Touch操作,你会发现,它的快捷widget有四个可跳转到APP内部各个功能的按钮。
支付宝的widget
那么这是怎么实现的呢?实际上iOS8开始引入的Extension,相对于母体APP,都是独立的,Extension管理自己的数据,母体APP管理自己的数据。那如何调起母体APP呢?其实很简单,正如我们自己的APP调起其他APP一样,比如微信、支付宝、微博等,那就是利用URL Scheme。
我们先为我们的母体APP加入一个URL Scheme。
为母体APP添加URL Scheme
widget中想调起母体APP,直接使用下面的OpenUrl就搞定了。
- (void)openApp {
NSURL *url = [NSURL URLWithString:@"todayDemo://"];
[self.extensionContext openURL:url completionHandler:nil];
}
这里有一个需要注意的点就是,widget作为一个Extension,是不存在Application的概念的,所以不能用传统的[UIApplication sharedApplication]的方式来进行openUrl操作。使用self.extensionContext,即可获取当前Extension上下文,也就是我们的widget。
母体APP需要在AppDelegate中的openUrl方法中拦截到widget的openUrl。我们看看获取到的url(下面的方法在iOS10舍弃了,但还是可以用的):
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
UIAlertView *av = [[UIAlertView alloc] initWithTitle:[url absoluteString] message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:nil, nil];
[av show];
return YES;
}
运行项目,点击widget后调起母体APP的效果为:
接收到的url
跟我们openUrl时填写的url一模一样。这样我们就可以在这个url后面拼接上各种参数,告诉母体APP跳转到哪个页面了。大致样式是这样的:
todayDemo://?params=gotoSomeView
跟一般的get请求的网址类似,可以拼接各种参数进去,在母体APP获取到url时,直接[url query]就能获取到"?"后面的所有参数。
与母体APP组成APP Group
我们的主要目的还是想widget和母体APP共享数据,在很多笔记APP中,widget和母体APP是共享笔记的,那我们就要用到APP Group了。我们先在母体APP中创建一个APP Group:
创建Group
创建完成后,再来到我们的Extension的target下。你会发现APP Group下多了之前我们在母体添加的Group,将它勾选上,则我们的Extension就和母体APP组成了APP Group。
Paste_Image.png
这个时候,两个程序,就可以共享一些数据了。我们以NSUserDefault为例,在母体中保存一个字段,然后在Extension中读取这个字段,识别到后,将widget的颜色变成红色。
首先在母体APP的ViewController中获取APP Group的UserDefault,并存储一个字段。
- (void)viewDidLoad {
[super viewDidLoad];
// 创建一个UserDefault
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.MyTodayGroup"];
// 存储一个颜色
[userDefaults setObject:@"redColor" forKey:@"color"];
// 同步
[userDefaults synchronize];
}
这里需要注意的是,我们平时项目中用的都是[NSUserDefaults standardUserDefaults],但这个UserDefault实际上是无法跟Extension互通的,我们需要找到APP Group所对应的UserDefault,注意initWithSuiteName后面接的名字,一定是我们之前创建的APP Group的名字。
然后我们来到widget中的controller,还记得之前我们遵守的协议吗,里面还有一个方法,就是更新widget的一个方法。方法中completionHandler之前的代码,就是我们正常的逻辑操作,匹配到字段后,将背景色替换为红色,然后completionHandler(NCUpdateResultNewData)这句,则是告诉widget我们处理完了,做了NCUpdateResultNewData的处理,更新了数据。这是个枚举值,具体含义可以点进去看下系统的注释。
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData
// 拿到主程序的NSUserDefault
NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.MyTodayGroup"];
NSString *color = [userDefaults objectForKey:@"color"];
if ([color isEqualToString:@"redColor"]) {
self.view.backgroundColor = [UIColor redColor];
}
completionHandler(NCUpdateResultNewData);
}
先运行母体APP,再运行widget,看下效果:
效果
总结
以上的包括widget的UI、调起母体APP、与母体APP数据互通这些特性组合在一起,便可满足我们正常的开发需求了。所以还等什么呢,快为你的APP加入widget特性吧!
网友评论