Today Extension
Today Extension(也叫Widget)
Today Extension创建步骤
开始之前先要创建一个iOS项目,因为Extension不能脱离containing app而存在。本项目实例名为,TodayExtensionDemo,项目创建完后
具体步骤如下:
File -> New -> Target 选择Today Extension,点击继续。
添加后,设置Active Scheme为刚创建的Extension,如下图
点击运行,点击运行后会出现一个选择框,选择Today就可以了
然后下拉通知栏,点击今天,点击编辑,就看到可以添加刚刚自己创建的扩展了,扩展显示的名字可以在刚刚的info.plist中修改。
调试扩展的方式有两种:
1、选择这个扩展的scheme直接运行
2、先运行容器App,然后下拉通知栏打开今日扩展,点击Xcode菜单的Debug->Attach To Process ,选择Likly Targets中需要调试的扩展
代码编写
接下来的工作就是自定义这个展示页面了,如果你习惯使用Storyboard直接在MainInterface.storyboard上修改即可,如果你习惯自己Coding,你需要先修改一下info.plist
修改方法:
// 删除
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
// 添加:
<key>NSExtensionPrincipalClass</key>
<string>TodayViewController</string>
//修改CFBundleDisplayName,调整Widget显示的名称
<key>CFBundleDisplayName</key>
<string>显示的名称</string>
1.调整Widget的高度
-(void)awakeFromNib {
[super awakeFromNib];
[self setPreferredContentSize:CGSizeMake(0, 240)];
}
取消widget默认的inset,让应用靠左
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets
{
return UIEdgeInsetsZero;
}
2.如果你要访问http站点链接,iOS9之后因为苹果App Transport Security (ATS)新特性,无法直接访问http数据,你也需要在Extension的plist中添加如下代码:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
3.因为Extension和containing app无法进行数据和文件共享,所以你还需要在Extenison中再添加一遍需要的文件。
4.在widget想要点击页面打开containing app。需要采用Open URL的方式打开containing app。
首先,在containing app的info.plist添加如下代码:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<!--这个一定要唯一-->
<string>com.wildcat.TodayExtensionDemo</string>
<key>CFBundleURLSchemes</key>
<array>
<!--调转URL的host,例如:TodayDemo:// --->
<string>TodayDemo</string>
</array>
</dict>
</array>
在today extension中实现:
-(void)openURLContainingAPP
{
[self.extensionContext openURL:[NSURL URLWithString:@"lecoding://action=GotoHomePage"]
completionHandler:^(BOOL success) {
NSLog(@"open url result:%d",success);
}];
}
在 containing app appdelegate中添加如代码,接收跳转:
-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options{
NSString* prefix = @"TodayDemo://";
if ([[url absoluteString] rangeOfString:prefix].location != NSNotFound) {
NSString* action = [[url absoluteString] substringFromIndex:prefix.length];
if ([action isEqualToString:@"GotoHomePage"]) {
}
else if([action isEqualToString:@"GotoOtherPage"]) {
}
}
return YES;
}
如何使用containing app中的图片
虽然Extention无法和containing app 公用库文件,但是可以公用图片,方法就是:
左侧选中containing app的Assets.xcassets,在右侧File Inspector中的Target Membership勾选Extension项目名。如下图:
在应用和扩展间共享数据 - App Groups
对 iOS 开发者来说,沙盒限制了我们在设备上随意读取和写入。但是对于应用和其对应的扩展来说,Apple 在 iOS 8 +中为我们提供了一种可能性,那就是 App Groups。App Groups 为同一个开发商的应用或者扩展定义了一组域,在这个域中同一个 group 可以共享一些资源。
首先我们需要开启 App Groups。选择主 Target,打开它的 Capabilities 选项卡,找到 App Groups 并打开开关,然后添加一个你能记得的 group 名字,比如group.myWidget。接下来你还需要为xxxWidget这个 Target 进行同样的配置,只不过不再需要新建 group,而是勾选刚才创建的 group 就行。
//读写共享的数据
- (NSUserDefaults *)loadGroupData
{
NSUserDefaults *userDefault = [[NSUserDefaults alloc] initWithSuiteName:@"group.myWidget"];// SuiteName必须和上面Capabilities配置填写的一致
return userDefault;
}
- (void)setVolume:(uint32_t)volume
{
[[self loadGroupData] setObject:@(volume) forKey:@"volume"];
[[self loadGroupData] synchronize];
}
- (uint32_t)getVolume:(uint32_t)volume
{
NSNumber *volumeNumber = [[self loadGroupData] objectForKey:@"volume"];
if (volumeNumber && [volumeNumber isKindOfClass:[NSNumber class]]) {
return volumeNumber.unsignedIntValue;
} else {
return kDefaultVolume;
}
}
widget向containing app 发送数据
//1、容器App设置定时器去轮询共享数据,一旦发生变化,就相应地改变音量。
//2、扩展发送请求到服务器,服务器再通知容器App。
//3.它就是 CFNotificationCenterGetDarwinNotifyCenter!这是CoreFoundation库中一个系统级的通知中心,苹果的系统自己也在用它,看清了“Darwin””了没有?哈哈!看了下CFNotificationCenter相关的API,跟NSNotificationCenter有点像。需要用到Toll-Bridge的知识与CoreFoundation相关的类进行桥接,这虽不常用但也不难。还需要注意下个别参数的使用。
// 接受不到数据,扩展发送通知前先存储数据,容器App接收到通知后,再读取共享数据那就好了
Containing App:
// 添加监听
- (void)addObserver
{
CFNotificationCenterRef notification = CFNotificationCenterGetDarwinNotifyCenter ();
CFNotificationCenterAddObserver(notification, (__bridge const void *)(self), observerMethod, CFSTR("通知名"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
}
// 监听widget改变
void observerMethod (CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
//监听到notification后要做的处理
}
// 移除监听
- (void)removeObserver
{
CFNotificationCenterRef notification = CFNotificationCenterGetDarwinNotifyCenter ();
CFNotificationCenterRemoveObserver(notification, (__bridge const void *)(self), CFSTR("通知名"), NULL);
}
Widget:
// 发送通知
- (void)postNotificaiton
{
CFNotificationCenterRef notification = CFNotificationCenterGetDarwinNotifyCenter ();
CFNotificationCenterPostNotification(notification, CFSTR("通知名"), NULL, NULL, YES);
}
BTW,如果想实现更复杂的功能,推荐使用MMWormhole这个开源库,它专门用于在Container app 与 Extension间传递消息,苹果婊 Watch OS 也适用~
最后分享一个Podfile多个target引用部分相同pod库的编写方法:
def host_pods
pod 'SSKeychain', '~> 0.1.4'
pod 'INAppStoreWindow', :head
pod 'AFNetworking', '1.1.0'
end
def shared_pods
pod 'MMWormhole','~> 2.0.0'
end
target 'HostApp' do
shared_pods
host_pods
end
target 'Extension' do
shared_pods
end
上一篇: Today Extension
下一篇: Toggle的监听注意事项
推荐阅读
-
iOS 10 Today Widget解析
-
iOS 10 Today Widget解析
-
vscode extension插件开发详解
-
IOS如何在Host App 与 App Extension 之间发送通知
-
C#基于Extension Method(扩展方法)获得文件大小的方法
-
Excel2010从函数库类别菜单中选择TODAY函数输入当天日期
-
PHP提示Deprecated: mysql_connect(): The mysql extension is deprecated的解决方法
-
百度凤巢上线Extension,搜索可直接显示粉丝数
-
vscode extension插件开发详解
-
[Php] windows下使用composer出现SHA384 is not supported by your openssl extension