ios8 widget

App Extension 概述

应用程序扩展是iOS8中引入的一个非常重要的新特性,扩展让app之间的数据交互成为可能.用户可以在app中使用其他应用提供的功能,而无需离开当前的应用.但是基于安全和性能的考虑,每一个扩展运行在一个单独的进程中,它拥有自己的bundle,bundle后缀名是”.appex””.(PS:当天听到最多的可能就是,不用越狱也能用第三方输入法了,搜狗和百度也第一时间推出了Custom keyboard)

iOS8上共有6个区域支持Extension,分别是Today、Share、Action、Photo Editing、Storage Provider、Custom keyboard几种,其中Today中的extension一般称为widget.
这里主要介绍widget(其他脑补…键盘最容易…)

先来看一张图:

分别是Clips墨迹天气搜狐视频3个App的Widget.
我们所要做的,也就是这样,根据Containing App来定制属于自己的Widget.

Start My Widget

我做的这个Demo是类似于Clips的widget,完整代码我已经上传到了github点这里,Demo里面注释比较详细.
1.创建Extension
 点击“File”->”New”->”Target”
 
2.后续步骤略略略略略
  涉及扩展如何运作Extension和Containing App、host app之间的关系Containing App与扩展共享数据开启App Groups等等知识点在查看到的资料中写得很详细,我就不打算详述(其实是水平有限^_^),接下来我重点讲下我在项目具体遇到的问题。(详细请见最后的参考资料,大牛们写得很棒!膜拜!)
3.My Problem
 (1)UI布局:系统默认,widget的View的x坐标是和Containing App的图标坐标的bottom相对应的(参照搜狐视频效果),如果你想靠到左边去“越界”,要实现NCWidgetProviding代理方法- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets,这个defaultMarginInsets打印出来是{0, 47, 39, 0},注意看x左边是0.

1234567
// 一般默认的View是从图标的右边开始的...如果你想变换,就要实现这个方法- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets {//UIEdgeInsets newMarginInsets = UIEdgeInsetsMake(defaultMarginInsets.top, defaultMarginInsets.left - 16, defaultMarginInsets.bottom, defaultMarginInsets.right);//return newMarginInsets;//return UIEdgeInsetsZero; // 完全靠到了左边....return UIEdgeInsetsMake(0.0, 16.0, 0, 0);}

 (2)View高度问题:有的时候运行程序,view显示不出来,这个时候你可能需要[self setPreferredContentSize:(CGSize)];。不仅如此,Demo中的widget是放置了个UITableView,设置它与View的AutoLayout,结果是没起作用。。。tableView的高度是随着Cell的减少而减少,但是View的高度缺固定在最初值。因此加上这句代码来限制:

12
// 调整高度,根据数组的值来确定Cell的个数,从而确定视图的高度self.preferredContentSize = CGSizeMake(self.view.bounds.size.width, 400-72*(5 - _allDataArray.count));

 (3)真机调试时,App Groups 会因为你用开发者账号进行调试而出现取消勾选的情况。如果真机调试失败,记得去检查这里。附上gif动图和调用不一样的NSUserDefaults进行固化的代码。
 

1234567891011121314151617181920
static NSString *const KArchiverKey = @"DataArray";// 数据处理(需要固化)// @"group.com.tranfer" 是entitlements中“com.apple.security.application-groups”对应的值NSUserDefaults *sharedUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.tranfer"];NSData *unarchiverData = [sharedUserDefaults objectForKey:KArchiverKey];// 特别注意,[NSKeyedUnarchiver unarchiveObjectWithData:unarchiverData] 转化的是一个数组,因为我固化的是一个数组// PS:allDataArray 的类型 是 NSMutableArray// 不能直接用self.allDataArray = [NSKeyedUnarchiver unarchiveObjectWithData:unarchiverData];self.allDataArray = [NSMutableArray arrayWithArray:[NSKeyedUnarchiver unarchiveObjectWithData:unarchiverData]];NSData *lastStringData = [sharedUserDefaults objectForKey:KArchiverLastClipKey];self.lastPasteBoard = [NSKeyedUnarchiver unarchiveObjectWithData:lastStringData];// 保存主数据- (void)_saveDataToSanBoxWithDataArray:(NSMutableArray *)dataArray {NSUserDefaults *sharedUserDefaults = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.tranfer"];NSData *archiverData = [NSKeyedArchiver archivedDataWithRootObject:dataArray];[sharedUserDefaults setObject:archiverData forKey:KArchiverKey];// 切莫忘记,依旧调用 synchronize 立即写入沙盒中[sharedUserDefaults synchronize];}

(4)在extension中打开containing app.调用下面方法:

1
 - (void)openURL:(NSURL *)URL completionHandler:(void (^)(BOOL success))completionHandler;

 接下来最关键的来了:
 

123
[self.extensionContext openURL:[NSURL URLWithString:@"iOSWidgetApp://"] completionHandler:^(BOOL success) {NSLog(@"open url result:%d",success);}];

 

上传注意事项

按照以往步骤上传应用就会报如下错误:

123
error: Embedded binary is not signed with the same certificate as the parent app. Verify the embedded binary target's code sign settings match the parent app's.		Embedded Binary Signing Certificate:	iPhone Developer: xxx xxx (H536M777VC)		Parent App Signing Certificate:		iPhone Distribution: xxx xxx (AA56B6R4CW)

  如果想要将带有Extension的应用上传到App Store,你需要为extension单独的申请一个AppID(Bundle ID要对应XCode里面extension的Bundle ID,千万别直接复制,会漏掉后面无法复制到呈灰白的字段),同时配备相对应的distribution profile.

Ending

  暂时就这些了,这也是我第一个Extension的Demo,大家如果发现什么问题,欢迎给我留言,小菜鸟的我仍在不断的探索中。


你可能感兴趣的:(ios8,widget)