title: 【iOS】Widget开发-开发篇
date: 2017-09-07 13:43
tags: iOS
blog
http://blog.zhangpeng.site
简书
http://www.jianshu.com/u/5690b3ad0a6f
Github
https://github.com/Zhang1Peng?tab=repositories
Widget开发步骤
建议先阅读Widget的配置篇,再开始开发,因为开发的过程中需要提前准备一些东西【iOS】Widget开发-配置篇
1. 创建新的Target,选择Today Extension。点击Next后,会生成如下图的几个文件
![](https://img.haomeiwen.com/i4835393/22b66f5e9060ade1.jpeg)
![](https://img.haomeiwen.com/i4835393/1169e0ba4f7a7dac.png)
2. 查看新生成的Info.plist
- Bundle display name:Widget在通知栏显示的名称
- NSExtension
如果你是使用纯代码进行开发,请按照下面进行操作:
(1)请删除NSExtensionMainStoryboard 的键值对和MainInterface.storyboard文件;
(2)请添加NSExtensionPrincipalClass这个key,并将value设置为控制器(如TodayViewController)
![](https://img.haomeiwen.com/i4835393/a9ae38ba56e7fed4.png)
3. 准备工作都已经完成,正式进入开发工作
iOS 8
①、iOS8下没有折叠和展开功能,默认的Widget高度为self.preferredContentSize设置的高度。
self.preferredContentSize = CGSizeMake(kScreenW, 100);
②、iOS8下所有组件默认右移30单位,可以通过下面的方法修改上下左右的距离
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets {
return UIEdgeInsetsMake(0, 0, 0, 0);
}
iOS 10
iOS 10以后,Widget可玩性更高了,有了两种显示模式
- NCWidgetDisplayModeCompact, // Fixed height,高度固定,最低高度为110
- NCWidgetDisplayModeExpanded, // Variable height,高度可变
// 5s模拟器下:
// NCWidgetDisplayModeCompact模式下:{304, 110}
// NCWidgetDisplayModeExpanded模式下:{304, 528}
// 6s模拟器下:
// NCWidgetDisplayModeCompact模式下:{359, 110}
// NCWidgetDisplayModeExpanded模式下:{359, 616}
设定显示模式,需要在设定Size前设定这个属性!!!
if ([[UIDevice currentDevice] systemVersion].intValue >= 10) {
self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeCompact;
// self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
viewDidLoad的代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
if ([[UIDevice currentDevice] systemVersion].intValue >= 10) {
self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeCompact;
}
self.preferredContentSize = CGSizeMake(kScreenW, 100);
[self setupUI];
}
当显示模式设置为NCWidgetDisplayModeExpanded时,点击折叠和打开时,会触发下面这个方法,在这个方法中可以修改对应状态的高度
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
if (activeDisplayMode == NCWidgetDisplayModeCompact) {
self.preferredContentSize = CGSizeMake(maxSize.width, 110);
}
else {
self.preferredContentSize = CGSizeMake(maxSize.width, 200);
}
}
在下面的方法中更新视图
-(void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// NCUpdateResultNewData 新的内容需要重新绘制视图
// NCUpdateResultNoData 部件不需要更新
// NCUpdateResultFailed 更新过程中发生错误
completionHandler(NCUpdateResultNoData);
}
开发中可能会遇上的一些问题
代码共享
目前我见到了四种共享代码的方法:
- 将代码打包成Framework,然后link到主App和Widget中(推荐)
- 不怕安装包变大的话,可以考虑将需要的第三方库在主App和Widget中分别复制一份(推荐)
-
将需要共享的文件按图中进行勾选配置
image
- 通过Pods导入,不太建议通过pod分别向两个Target中导入第三方库,因为很容易发生一些不好处理的问题
数据共享
数据共享有两种常用的方法:
- NSUserDefaults,和我们常用的方法一样,不过在创建NSUserDefaults时,需要填写我们之前的GroupID。通过GroupID,我们就可以进行主App和Widget之间的数据共享了。
/// 写入数据
NSString *groupID = @"group.com.aaa.bbb";
NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:groupID];
[ud setObject:@"我是测试的数据" forKey:@"test"];
[ud synchronize];
/// 读取数据
NSString *groupID = @"group.com.aaa.bbb";
NSUserDefaults *ud = [[NSUserDefaults alloc] initWithSuiteName:groupID];
NSString *value = [ud objectForKey:@"test"];
- NSFileManager
/// 写入数据
NSString *groupID = @"group.com.aaa.bbb";
NSError *err = nil;
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupID];
containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];
NSString *value = @"我是测试的数据";
BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&err];
if(result){
NSLog(@"写入成功");
}
/// 读取数据
NSString *groupID = @"group.com.aaa.bbb";
NSError *err = nil;
NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupID];
containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];
NSString *value = [NSString stringWithContentsOfURL:containerURL encoding:NSUTF8StringEncoding error:&err];
打开App
-
设置App的URLSchemes,打开APP主要通过URLScheme打开和传递参数值。
设置URLSchemes时,要独特一些,避免与其他App重复
image
-
在Widget中添加点击事件,用于触发打开App的操作和传递参数
NSString *schemeString = @"zhangpeng://actionName?paramName=paramValue";
[self.extensionContext openURL:[NSURL URLWithString:schemeString] completionHandler:^(BOOL success) {
}];
- app的appdelegate的代理方法中,截取URL,做响应处理:
// 所有版本的都可以使用
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[self appCallbackWithOpenUrl:url];
return YES;
}
/// iOS 8 以后
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
[self appCallbackWithOpenUrl:url];
return YES;
}
/// iOS 7
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
[self appCallbackWithOpenUrl:url];
return YES;
}
- (void)appCallbackWithOpenUrl:(NSURL *)url{
NSLog(@"url: %@", url.host);
// 针对url进行不同的操作
}
数据刷新
当widget从屏幕上消失2s左右,再次出现在屏幕中时,都会重新调用viewDidLoad方法。所以每次出现都请求最新数据,进行刷新操作,widget都会闪一下,根据产品需求,可以做一下控制;
- (void)viewDidLoad {
[super viewDidLoad];
}
如果短时间内让Widget频繁地消失显示,那只会执行viewWillAppear方法;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
网友评论