您的位置:首页 > 移动开发 > IOS开发

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