第一次寫有關(guān)技術(shù)的博客,有錯(cuò)誤的地方請(qǐng)及時(shí)糾正。
效果圖:

最近寫了一個(gè)app需要用到app擴(kuò)展(App Extension),本次主要講的是Today這個(gè)擴(kuò)展,關(guān)于Toady的官方介紹自己先看,廢話不多說(shuō)直接寫代碼,竟然寫了那就從申請(qǐng)appID開始:
1.先新建一個(gè)項(xiàng)目。。。。。
2.申請(qǐng)ID:


然后點(diǎn)擊注冊(cè)完成;
3申請(qǐng)App Groups



去iOS App IDs這個(gè)界面找到Name為:BlobBlobWidget的ID,點(diǎn)擊編輯
然后


4.找到ID發(fā)現(xiàn)下面都變綠說(shuō)明配置成功了,如果不是的話,估計(jì)你得重復(fù)在來(lái)一次

5開始代碼項(xiàng)目中的配置
新建擴(kuò)展

先配置General

在配置Capabilities

好了以上是宿主app也就是主app配置完成。
6.下面配置TodayExtension

在配置Capabilities

以上都配置完了運(yùn)行以下,正常了就是正常的,不正常就是不正常了,順便自己看看哪里出的問(wèn)題,bug在哪里自己心里最清楚,別人都是愛莫能助啊。
7.關(guān)于調(diào)試
在調(diào)試的時(shí)候xcode會(huì)彈出一個(gè)調(diào)試框框,讓你選擇一個(gè)主APP。主APP就是要選擇你建立的APP;
不知道看懂沒有。
8數(shù)據(jù)的共享
8.1通過(guò)用NSUserDefaults共享數(shù)據(jù)
存儲(chǔ):
#pragma mark存儲(chǔ)data
- (void)saveData:(UITextField*)TF{
NSUserDefaults* defaults = [[NSUserDefaultsalloc]initWithSuiteName:@"group.DSLBlobWidget"];
[defaultssetObject:TF.textforKey:@"DSL"];
[defaultssynchronize];
}
獲取:
#pragma mark從app那里加載數(shù)據(jù)以便達(dá)到數(shù)據(jù)共享的目的
- (NSString*)getDatafromMainAPP{
NSUserDefaults*defaults = [[NSUserDefaultsalloc]initWithSuiteName:@"group.DSLBlobWidget"];
return[defaultsobjectForKey:@"DSL"];
}
8.2通過(guò)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那里加載數(shù)據(jù)以便達(dá)到數(shù)據(jù)共享的目的(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.代碼的共享
第一步:和創(chuàng)建APP Extension一樣,New->Target,選擇Cocoa Touch Framework來(lái)創(chuàng)建framework
第二部:起名字,我的起的名字是DSLWidgetFramework
第三步:

10在APP Extension中不能使用的API
10.1Access a sharedApplication object, and so cannot use any of the methods on that object
不能獲取sharedApplication對(duì)象
10.2Use any API marked in header files with theNS_EXTENSION_UNAVAILABLEmacro, or similar unavailability macro, or any API in an unavailable framework,F(xiàn)or example, in iOS 8.0, the HealthKit framework and EventKit UI framework are unavailable to app extensions.
不能使用API的標(biāo)志性頭文件 和 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)
不能獲取麥克風(fēng)和照相機(jī)的權(quán)限(iMessage 不同于其它的應(yīng)用擴(kuò)展,只要配置相關(guān)的應(yīng)用權(quán)限就可以直接使用了)
10.4Perform long-running background tasks
不能長(zhǎng)期的在后臺(tái)運(yùn)行
10.5Receive data using AirDrop
不能使用AirDrop接收相關(guān)的數(shù)據(jù)
以上個(gè)人翻譯,建議查看官方文檔
11 .TodayExtension的進(jìn)入設(shè)置的快捷方式
//打開Wi-Fi
[self.extensionContextopenURL:[NSURLURLWithString:@"Prefs:root=WIFI"]completionHandler:^(BOOLsuccess) {
}];
//打開蜂窩網(wǎng)絡(luò)
[self.extensionContextopenURL:[NSURLURLWithString:@"Prefs:root=MOBILE_DATA_SETTINGS_ID"]completionHandler:^(BOOLsuccess) {
}];
請(qǐng)盡量按照以上方式避免被拒絕。
12.與主APP的交互

在擴(kuò)展中的代碼:
- (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"]) {
//判斷是否是直接跳入到添加頁(yè)面
//self.window.rootViewController = [[SecondVC alloc]init];
UIViewController*rootNav = (UIViewController*)self.window.rootViewController;
[rootNavpresentViewController:[[SecondVCalloc]init]animated:YEScompletion:nil];
}
}
returnYES;
}
13.折疊與展開
首先折疊與展開只有iOS10才有,
所以先要判斷系統(tǒng)的版本
if ([[UIDevice currentDevice].systemVersion integerValue]>=10) {
self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
然后實(shí)現(xiàn)協(xié)議,點(diǎn)擊展開或者是折疊會(huì)觸發(fā)這個(gè)協(xié)議就是NCWidgetProviding協(xié)議
- (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{
//因?yàn)闄C(jī)子型號(hào)不一樣所以最大搞多可能不一樣,這里設(shè)置最大
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width ,MAXFLOAT);
}
}
除了上面的協(xié)議外還有一個(gè)協(xié)議就是更新擴(kuò)展UI界面的協(xié)議
該協(xié)議的具體用法,我沒有探究,需要更新這個(gè)界面的時(shí)候就在這方法里面寫
- (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.關(guān)于使用StoryBoard或者是代碼
14.1使用StryBoard如果使用自己定義的類的StoryBoard的話,需要修改info文件

14.2如果是代碼的話
你得把NSExtensionMainStoryboard字段刪除,添加NSExtensionPrincipalClass字典,value為是你自己建立的類的名稱

使用這個(gè)方法不要忘記在todayViewController的ViewDidLoad中設(shè)置preferredContentSize屬性調(diào)整大小。
14關(guān)于上架,上架的時(shí)候擴(kuò)展app一定是單獨(dú)的appID,且命名時(shí)比如:主appID是com.DSLWidget那么擴(kuò)展appID是com.DSLWidget.widExten這樣盡量按照這個(gè)格式來(lái),不要問(wèn)我為什么之前上架踩過(guò)的坑
本次關(guān)于Today Extension之旅結(jié)束。隨后補(bǔ)充。有錯(cuò)請(qǐng)立即通知我,謝謝。
我叫董詩(shī)磊就是一碼農(nóng)。