第一次写有关技术的博客,有错误的地方请及时纠正。
效果图:
最近写了一个app需要用到app扩展(App Extension),本次主要讲的是Today这个扩展,关于Toady的官方介绍自己先看,废话不多说直接写代码,竟然写了那就从申请appID开始:
1.先新建一个项目。。。。。
2.申请ID:
然后点击注册完成;
3申请App Groups
去iOS App IDs这个界面找到Name为:BlobBlobWidget的ID,点击编辑
然后
4.找到ID发现下面都变绿说明配置成功了,如果不是的话,估计你得重复在来一次
5开始代码项目中的配置
新建扩展
先配置General
在配置Capabilities
好了以上是宿主app也就是主app配置完成。
6.下面配置TodayExtension
在配置Capabilities
以上都配置完了运行以下,正常了就是正常的,不正常就是不正常了,顺便自己看看哪里出的问题,bug在哪里自己心里最清楚,别人都是爱莫能助啊。
7.关于调试
在调试的时候xcode会弹出一个调试框框,让你选择一个主APP。主APP就是要选择你建立的APP;
不知道看懂没有。
8数据的共享
8.1通过用NSUserDefaults共享数据
存储:
#pragma mark存储data
- (void)saveData:(UITextField*)TF{
NSUserDefaults* defaults = [[NSUserDefaultsalloc]initWithSuiteName:@"group.DSLBlobWidget"];
[defaultssetObject:TF.textforKey:@"DSL"];
[defaultssynchronize];
}
获取:
#pragma mark从app那里加载数据以便达到数据共享的目的
- (NSString*)getDatafromMainAPP{
NSUserDefaults*defaults = [[NSUserDefaultsalloc]initWithSuiteName:@"group.DSLBlobWidget"];
return[defaultsobjectForKey:@"DSL"];
}
8.2通过NSFileManger存图片
//copy image to Library
- (void)copyImageToLibrary{
NSString * path = [[NSBundle mainBundle]pathForResource:@"DSL.png" ofType:nil];
NSFileManager * manger = [NSFileManager defaultManager];
NSString * documentPath = [NSHomeDirectory() stringByAppendingPathComponent:@"/Library/Caches/DSL.png"];
NSLog(@"%@",documentPath);
NSError * error = nil;
if (path != nil) {
if (![manger fileExistsAtPath:documentPath]) {
[manger copyItemAtPath:path toPath:documentPath error:&error];
if (error) {
NSLog(@"==copy error==%@",[error
localizedDescription]);
}
}
}else{
NSLog(@"sourcePath is nil");
}
[self copyImage];
}
#pragma mark从app那里加载数据以便达到数据共享的目的(NSFileManger 存图片)
- (void)copyImage{
NSError * error = nil;
NSURL * contentURL = [[NSFileManager defaultManager]containerURLForSecurityApplicationGroupIdentifier:@"group.DSLBlobWidget"];
NSString * newPath = [contentURL URLByAppendingPathComponent:@"/Library/Caches/DSL.png"].path;
[[NSFileManager
defaultManager]copyItemAtPath:[NSHomeDirectory()
stringByAppendingPathComponent:@"/Library/Caches/DSL.png"]
toPath:newPath error:&error];
if (error) {
NSLog(@"copy error==%@",[error localizedDescription]);
}
}
9.代码的共享
第一步:和创建APP Extension一样,New->Target,选择Cocoa Touch Framework来创建framework
第二部:起名字,我的起的名字是DSLWidgetFramework
第三步:
10在APP Extension中不能使用的API
10.1Access a sharedApplication object, and so cannot use any of the methods on that object
不能获取sharedApplication对象
10.2Use any API marked in header files with theNS_EXTENSION_UNAVAILABLEmacro, or similar unavailability macro, or any API in an unavailable framework,For example, in iOS 8.0, the HealthKit framework and EventKit UI framework are unavailable to app extensions.
不能使用API的标志性头文件 和 theNS_EXTENSION_UNAVAILABLE 宏定义,还有一些不能用的框架API,例如HealthKit framework and EventKit UI framework。
10.3Access the camera or microphone on an iOS device (an iMessage app, unlike other app extensions, does have access to these resources, as long as it correctly configures theNSCameraUsageDescriptionandNSMicrophoneUsageDescriptionInfo.plistkeys)
不能获取麦克风和照相机的权限(iMessage 不同于其它的应用扩展,只要配置相关的应用权限就可以直接使用了)
10.4Perform long-running background tasks
不能长期的在后台运行
10.5Receive data using AirDrop
不能使用AirDrop接收相关的数据
以上个人翻译,建议查看官方文档
11 .TodayExtension的进入设置的快捷方式
//打开Wi-Fi
[self.extensionContextopenURL:[NSURLURLWithString:@"Prefs:root=WIFI"]completionHandler:^(BOOLsuccess) {
}];
//打开蜂窝网络
[self.extensionContextopenURL:[NSURLURLWithString:@"Prefs:root=MOBILE_DATA_SETTINGS_ID"]completionHandler:^(BOOLsuccess) {
}];
请尽量按照以上方式避免被拒绝。
12.与主APP的交互
在扩展中的代码:
- (void)OpenAcstion{
[self.extensionContextopenURL:[NSURLURLWithString:@"DSLTodayWidget://SecondVC"]completionHandler:^(BOOLsuccess) {
}];
}
在主APP中的代码:
AppDelegate
- (BOOL)application:(UIApplication*)app openURL:(NSURL*)url options:(NSDictionary *)options
{
NSLog(@"=====%@",url.scheme);
NSLog(@"===%@===%@",url.host,url.absoluteString);
if([url.absoluteStringhasPrefix:@"DSLTodayWidget"]) {
if([url.hostisEqualToString:@"SecondVC"]) {
//判断是否是直接跳入到添加页面
//self.window.rootViewController = [[SecondVC alloc]init];
UIViewController*rootNav = (UIViewController*)self.window.rootViewController;
[rootNavpresentViewController:[[SecondVCalloc]init]animated:YEScompletion:nil];
}
}
returnYES;
}
13.折叠与展开
首先折叠与展开只有iOS10才有,
所以先要判断系统的版本
if ([[UIDevice currentDevice].systemVersion integerValue]>=10) {
self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
然后实现协议,点击展开或者是折叠会触发这个协议就是NCWidgetProviding协议
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize
{
// NCWidgetDisplayModeCompact, // Fixed height
// NCWidgetDisplayModeExpanded,
if (activeDisplayMode == NCWidgetDisplayModeCompact ) {
//高度最低为110
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width ,110);
}else{
//因为机子型号不一样所以最大搞多可能不一样,这里设置最大
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width ,MAXFLOAT);
}
}
除了上面的协议外还有一个协议就是更新扩展UI界面的协议
该协议的具体用法,我没有探究,需要更新这个界面的时候就在这方法里面写
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// Perform any setup necessary in order to update the view.
// If an error is encountered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData
completionHandler(NCUpdateResultNewData);
}
14.关于使用StoryBoard或者是代码
14.1使用StryBoard如果使用自己定义的类的StoryBoard的话,需要修改info文件
14.2如果是代码的话
你得把NSExtensionMainStoryboard字段删除,添加NSExtensionPrincipalClass字典,value为是你自己建立的类的名称
使用这个方法不要忘记在todayViewController的ViewDidLoad中设置preferredContentSize属性调整大小。
14关于上架,上架的时候扩展app一定是单独的appID,且命名时比如:主appID是com.DSLWidget那么扩展appID是com.DSLWidget.widExten这样尽量按照这个格式来,不要问我为什么之前上架踩过的坑
本次关于Today Extension之旅结束。随后补充。有错请立即通知我,谢谢。
我叫董诗磊就是一码农。