iOS的Extension Widget开发:Today
2016-12-07 本文已影响160人
大斌小姜
[TOC]
1、Tips
-
扩展App想要使用主App中的代码,需要在如下位置引入
01.png -
调试主App则运行主App;调试扩展则运行扩展 (解决扩展不走断点的问题)
-
扩展App想要使用的图片资源等,需要引入到扩展文件夹下
2、纯代码需要配置info.plist的俩项参数
移除
NSExtensionMainStoryboard
键,并添加NSExtensionPrincipalClass
键,使用view controller的名字作为值。
3、UI样式
- 背景:尽量不要使用背景,默认的毛玻璃效果很好,主要文字颜色最好是白色,次要文字的颜色最好是
lightTextColor
- 不要在今日面板里使用可以滚动的 Scroll View,而是要完全展开
- 缩进:尽量保持默认的缩进,即左边会空几个像素。如果想改变默认缩进,在TodayViewController里面实现以下方法
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets: (UIEdgeInsets)defaultMarginInsets {
return UIEdgeInsetsZero;
}
4、与主App进行交互
- Today跳转App(唤起App,调用App某项功能)
[UIApplication sharedApplication]在扩展App中是无法访问的,需要通过NSExtensionContext来调用主App的openURL方法
//
[self.extensionContext openURL:[NSURL URLWithString:@"跳转链接"] completionHandler:^(BOOL success) {
NSLog(@"open url result:%d",success);
}];
02.png
跳转链接示例:iMyApp://
为跳转页面做标识
- App 处理URL Schemes
// iOS 9+
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options {
// [url.absoluteString hasPrefix:@"iMyApp://"]
if ([url.host isEqualToString:@"iMyApp"]) {
// 操作
return YES;
}
return YES;
}
// iOS 7、iOS 8
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
// [url.absoluteString hasPrefix:@"iMyApp://"]
if ([url.host isEqualToString:@"iMyApp"]) {
// 操作
return YES;
}
return YES;
}
5、与主App共享数据
- 利用group,将主App和扩展App做一个数据共享空间(
NSUserDefault
),先在主App的Targets中创建并设置,再在扩展App的Targets设置(如图)
- 主App存扩展App所需要数据
NSUserDefaults* userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.iMyApp"];
[userDefault setBool:YES forKey:@"islogin"];
- 扩展App取所需要数据
NSUserDefaults *myDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.iMyApp"];
BOOL isLogin = [myDefaults objectForKey:@"islogin"];
6、 - (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult result))completionHandler
方法说明
对于扩展App,即使扩展App现在不可见 (即用户没有拉开通知中心),系统也会时不时地调用实现了 NCWidgetProviding 的扩展的这个方法,来要求扩展刷新界面。
这个机制和 iOS 7 引入的后台机制是很相似的。在这个方法中我们一般可以做一些像 API 请求之类的事情,在获取到了数据并更新了界面,或者是失败后都使用提供的 completionHandler 来向系统进行报告
7、定时更新机制(通过增加定时更新的NSTimer)
把NSTimer fire触发代码调用放到
viewWillAppear
方法中来(viewDidLoad
方法并不是每次都执行).同理当Widget关闭后在viewDidDisappear
方法取消NSTimer invalidate定时更新即可。
8、关闭today widget中扩展App的显示
有时候在没有数据的时候需要隐藏扩展,可以使用以下方法:
NCWidgetController *widgetController = [NCWidgetController widgetController];
[widgetController setHasContent:NO forWidgetWithBundleIdentifier:@"扩展的id"];
9、iOS10 的适配- 展开、折叠按钮
在NSExtensionContext中,新添了
widgetLargestAvailableDisplayMode
属性,来确认当前widget是展开还是折叠状态。所以,先在viewWillAppear中设置widget的mode为展开。
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);
// 处理~~
}
}
- 如何用XCode 7.3打出能够适配iOS 10的widget呢?
Xcode 7没有iOS 10,可以通过KVC来解决这个问题
[self.extensionContext setValue:@"1" forKey:@"widgetLargestAvailableDisplayMode"];
if (activeDisplayMode == 0) {
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 110);
} else {
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 300);
}
}
- 切记:UI的更新要在主线程操作
//通知主线程刷新
dispatch_async(dispatch_get_main_queue(), ^{
//...........;
});