尽管iOS 8还未正式发布,但其新增许多特性已经让广大开发人员兴奋不已。Extension是iOS 8开放出来的最重要的新功能之一,意味着苹果这个封闭的系统向开放性迈出了一大步。这篇文章主要介绍Extension中的Widget,考虑到准确性,很多地方直接采用了苹果官方描述,并未将其翻译成中文,请见谅。
说明:本文基于当前XCode最新版本:XCode 6 Beta3
1. Extension
Extensions(扩展/插件):Lets you extend custom functionality and content beyond your app and make it available to users while they’re using other apps. You create an extension to enable a specific task; after users get your extension, they can use it to perform that task in a variety of contexts.
Extension point(扩展点): iOS and OS X define several general types of extensions, each of which is tied to an area of the system, such as sharing, Notification Center, and the iOS keyboard. A system area that supports extensions is called an extension point.
Widgets:通知中心的今日栏目的Extension被称为Widgets
2. Extension类型
3. Extension提交和激活
Extension提交
Extension只能被捆绑在应用内,Extension只能随着其Containing app一起被安装。所以用户不可以单独下载 Extension,开发者也不能向App Store提交单独的Extension
Extension激活
不同类型的Extension,激活方式也不同
- Today:在通知中心里进行激活或关闭;
- Custom Keyboard:在设置的相应区域里进行设置;
- Photo Editing和Storage Provider:只会在特定的情形下才会出现(比如你在使用照片应用时,或者你在选取文件时);
- Share和Action:能够在任何应用里被唤起,但开发者必须为扩展添加激活原则(Activation Rules),使得Extension只有在用户执行特定操作(比如分享链接或文档)时才会出现。
4. Widgets
设计实现Widget时需要注意的一些事情,总结如下:
- Ensure that content always looks up to date.
- Respond appropriately to user interactions.
- Perform well (widgets must use memory wisely or the system may terminate them)
- A widget may also appear on the lock screen of an iOS device.
- It’s a good idea to limit the number of interactive items in a widget.
- iOS widgets don’t support keyboard entry.
- Avoid putting a scroll view inside a widget.
- If you want to create an extension that enables a multistep task or helps users perform a lengthy task, such as uploading or downloading content, the Today extension point is not the right choice.
- Users need to be able to use the containing app to configure a widget’s content and behaviour.
5. Widget开发实现
5.1 使用Today Extension模版
一直点下一步直到结束,Today模版将自动生成下列默认文件
- OC类:TodayViewController.h/.m
- Plist文件:Info.plist
- UI文件:一个storyboard或者xib文件
开发人员也可以不使用storyboard,直接使用TodayViewController来生成UI,只需要将Plist文件中的NSExtensionMainStoryboard删除,添加NSExtensionPrincipalClass及属性值:
5.2 Widget UI相关方法
推荐使用 Auto Layout
调整边距: 使用 NCWidgetProviding 的widgetMarginInsetsForProposedMarginInsets:方法.
调整 widget 高度:
- 使用 Auto Layout
- 修改 UIViewController 的属性 preferredContentSize.-
动画函数:
- viewWillTransitionToSize:withTransitionCoordinator:
- animateAlongsideTransition:completion:
- notificationCenterVibrancyEffect.
5.3 Widget内容更新
Today extension point 提供了 Notification Center Framework 用于管理widget的状态和更新其内容。
更新widget内容使用 NCWidgetProviding protocol的widgetPerformUpdateWithCompletionHandler:方法
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// Perform any setup necessary in order to update the view.
NSLog(@"widgetPerformUpdateWithCompletionHandler");
// If an error is encoutered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData
self.value.text = [NSString stringWithFormat:@"$%d", arc4random() % 100];
completionHandler(NCUpdateResultNewData);
}
5.4 从Widget返回Containing app
[self.extensionContext openURL:[NSURL URLWithString:[NSString stringWithFormat:@"widgetExample://%@", value]]
completionHandler:nil];
extensionContext是iOS 8为UIViewController新增的属性:
@interface UIViewController(NSExtensionAdditions) <NSExtensionRequestHandling>
// Returns the extension context. Also acts as a convenience method for a view controller to check if it participating in an extension request.
@property (nonatomic,readonly,retain) NSExtensionContext *extensionContext NS_AVAILABLE_IOS(8_0);
@end
5.5 指定Widget什么时候显示
使用 NCWidgetController API 指定Widget是否应该显示。Widget 和 containing app 均可调用setHasContent:forWidgetWithBundleIdentifier: 方法,以通知widget是否有内容需要显示
NCWidgetController *controller = [NCWidgetController widgetController];
[controller setHasContent:YES forWidgetWithBundleIdentifier:@"org.muhan.WidgetDemo.myWidget"];
6. Containing app 与 Host app
-
Containing app: 尽管苹果开放了extension,但是在iOS中extension并不能单独存在,要想提交到AppStore,必须将extension包含在一个app中提交,这个包含extension的app就叫containing app。一个containing app可以包含多个extension。
-
Host app:能够调起extension的app被称为host app,host app发出一个request,触发extension生命周期的开始,并且host app将定义extension后续操作的上下文,当extension完成host app的请求时,其生命周期结束。
7. Extension和Host App之间通信
Extension和host app之间可以通过extensionContext属性直接通信,该属性是新增加的UIViewController类别,见上面5.4。由于Widget的Host app是通知中心,一般情况下不需要关心这两者之间的数据传输。
8. Extension和Containing App之间数据共享
- 尽管extension的bundle是放在containing app的bundle中,但是他们是两个完全独立的进程,两者不能直接通信
- iOS 8新增App Groups实现containing app和extension之间的数据共享。此外,App Group还支持同一个开发团队开发的不同app之间的数据共享,见另一篇文档:iOS 8 之 App Group。