iOS -- Widget 开发之 Today Extension
2016-12-13 15:17
435 查看
效果图:
创建 Today Extension:
先创建一个最基本的项目,项目创建完成之后,选中项目文件,点击 File–>New–>Target…,如下图。选中 Today Extension 项,然后点击 Next,命名(本文中为TodayExtension)。
在弹出框中选择 Activate,激活这个 scheme。激活之后,项目中就会多出一个TodayExtension 的扩展,还有如下图左侧的 TodayExtension 文件夹。
选中 TodayExtension 为 Target 后直接运行。
你就会看到下面的界面(如果开始没有出现这个界面,多试几次,因为它需要刷新)。
如果想要共享应用的图标,首先选中 Assets.xcassets,在 File Inspector 中的 Target Membership 中勾选 Today Extension,如下图。
效果如下:
Widget 布局方式:
如果牵扯到 UI 布局的方式,这里只需要调整一点东西即可。开发者可以选择使用 storyboard 完成快速布局,也可以选择使用代码来完成布局。只需要对扩展应用的 info.plist 文件做如下操作:使用 Interface Builder:这个是默认的,如果修改了默认的 storyboard,只需要将 NSExtensionMainStoryboard 的 value 值修改成相应的 storyboard 名字即可,如下图。
使用代码:首先将 NSExtensionMainStoryboard 字段删除,然后添加 NSExtensionPrincipalClass 字段,并将 value 值设置为主控制器的类名即可,如下图。
扩展和宿主应用之间共享数据:
很多时候我们需要扩展与宿主应用之间共享一些数据,对于数据共享,如果是单一的应用,我们有很多种方法来实现,比如单例、文件等形式,但由于扩展与宿主应用是两个完全独立的应用,并且 iOS 应用是基于沙盒的形式,所以一般的共享数据的方法实现不了数据共享,这里就需要使用 App Groups。首先需要在开发者网站注册一个 App Groups。
把 App Groups 分别添加到扩展和宿主应用的 App ID 中。
在宿主应用以及扩展应用中都将 App Groups 打开,选中需要共享数据的 group。
完成以上步骤后就可以进行数据共享,有两种共享数据的方法:
使用 NSUserDefault:这里不能使用
[NSUserDefaults standardUserDefaults];方法来初始化 NSUserDefaults 对象,正像之前所说,由于沙盒机制,扩展应用是不允许访问宿主应用的沙盒路径的,因此不能使用上述用法进行初始化,需要搭配 App Groups 完成实例化 NSUserDefaults。正确的使用方式如下:
写入数据
// NSUserDefaults + (void)saveDataByNSUserDefaultsWithNSArray:(NSArray *)dataAry { NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.chuanglong.widget"]; [userDefaults setValue:dataAry forKey:@"MyNote"]; [userDefaults synchronize]; }
读取数据
// NSUserDefaults + (NSArray *)readDataFromNSUserDefaults { NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.chuanglong.widget"]; NSArray *myNoteAry = [userDefaults valueForKey:@"MyNote"]; return myNoteAry; }
使用 NSFileManager:
写入数据
// NSFileManager + (void)saveDataByNSFileManagerWithNSArray:(NSArray *)dataAry { NSURL *containerUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.chuanglong.widget"]; containerUrl = [containerUrl URLByAppendingPathComponent:@"Library/Caches/MyNote"]; [dataAry writeToURL:containerUrl atomically:YES]; }
读取数据
// NSFileManager + (NSArray *)readDataFromNSFileManager { NSURL *containerUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.chuanglong.widget"]; containerUrl = [containerUrl URLByAppendingPathComponent:@"Library/Caches/MyNote"]; NSArray *myNoteAry = [NSArray arrayWithContentsOfURL:containerUrl]; return myNoteAry; }
这样就已经将数据存在一个可共享的地方,扩展和宿主应用都是从一个地方取数据,现在我们在宿主应用中添加一条记事,然后就可以下拉打开通知中心进行查看了,如下图。
扩展和宿主应用之间共享代码:
在我的这个应用中,记事列表的 cell 相对比较简单,但是如果我们需要使用复杂的 cell,而一般在通知中心扩展中,我们的 UI 有很大部分是跟宿主应用相同的,但是我们的扩展和宿主应用并不在一个 Target 当中,这也就代表着同样的代码,在扩展和宿主应用中我们需要分开两份,这样显然是很不合理的。所以,我们需要在扩展和宿主应用之间共享代码,也就是将所有公用的文件添加到一个 framework 中,这样我们只要在扩展中引入这个 framework,就可以使用其中的代码了。我将应用中列表的 datasource 和扩展中列表的 datasource 抽出来成为一个 NoteList 类,因为扩展和宿主应用都是同一份数据,所以我只要在这个 NoteList 类中实现一次 tableview 的代理方法,然后生成一个 framework,在扩展和宿主应用中引入就可以同时使用了。
首先,选中项目文件,点击 File–>New–>Target…,选择 Cocoa Touch Framework,创建一个 framwork,取名为 TableKit,如下图。
选中 .h 文件,在右侧 Target Membership 中选中我们所创建的 framwork,如下图。
选中 .m 文件,在右侧 Target Membership 中先取消 WidgetDemo 的 Target,然后选中我们所创建的 framwork,如下图。
如果你有许多公用的类想要生成 framwork,按照上述的步骤,将所有的文件 Target 都加入到所创建的 framwork 就行了。或者你也可以在创建公用的类时选择已经创建好的 framework 即可实现。
完成上述步骤之后,就可以在扩展和宿主应用中引入 TableKit,使用其中的类了。
从扩展中打开宿主应用:
首先需要配置 url scheme,这个在定义的时候尽量不要和其它应用冲突,本文中定义的是 TodayExtension。这样,通过访问 TodayExtension:// 就可以实现应用唤醒了,如下图。Apple 给每个 UIViewController 加了一个 extensionContext 属性,在我们的宿主应用中,这个属性是 nil,而在扩展中,我们就可以通过
[self.extensionContext openURL:[NSURL URLWithString:@"TodayExtension://"] completionHandler:nil];方法来打开宿主应用,代码如下。
- (void)headerView:(WidgetHeaderView *)headerView clickTag:(NSInteger)clickTag { NSString *urlStr = [NSString stringWithFormat:@"TodayExtension://%ld", clickTag-100]; NSURL *url = [NSURL URLWithString:urlStr]; [self.extensionContext openURL:url completionHandler:^(BOOL success) { }]; }
然后我们可以在 AppDelegate 的 openURL 代理方法中,通过 URL 后面的参数不同,来打开不同的页面,本文中比较简单,只有一个页面。
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { if ([url.scheme isEqualToString:@"TodayExtension"]) { return YES; } return NO; }
Demo 下载地址
相关文章推荐
- iOS之widget开发(Today Extension)
- iOS开发------Widget(Today Extension)插件化开发
- iOS Widget && Today Extension 开发
- iOS开发-widget基础
- iOS Widget开发
- ios 开发之widget实现
- iOS Widget开发
- iOS 开发之Widget的开发及使用(上)
- iOS开发之widget实现
- iOS Widget开发遇到的坑
- iOS开发之构建Widget
- ios 10 开发-widget实现
- IOS开发系列——Widget专题【整理】
- iOS 10 —— widget开发详解
- iOS Today Extension开发(Widget)
- iOS开发之构建Widget
- iOS开发之构建Widget
- iOS开发之widget实现详解
- iOS开发 widget构建详解及实现代码
- iOS widget开发