需求
作出下圖效果(上半部分)
首先我們知道這是一種Today Extension巩剖,上圖是通過3DTouch觸按彈出的铝穷,我們也可以在今日通知欄里添加看到。
最終的效果如下:
創(chuàng)建步驟
1佳魔、創(chuàng)建Today Extension
2曙聂、實(shí)現(xiàn)擴(kuò)展和宿主App之間共享數(shù)據(jù)
3、使用宿主App中的資源
4鞠鲜、擴(kuò)展中打開宿主App
5卢厂、補(bǔ)充:讀取xib文件、擴(kuò)展中支持三方框架源祈、參數(shù)傳遞猛拴、擴(kuò)展Widget高度、上架注意事項(xiàng)
創(chuàng)建Today Extension
首先霞捡,我們選中項(xiàng)目文件坐漏,選擇 xcode ->Editor ->Add Target,如下圖碧信,選中Today Extension項(xiàng)赊琳,然后點(diǎn)擊Next,命名(本文中為MyTodayWidget)砰碴,在彈出框中選擇Activate躏筏,激活這個(gè)scheme。
激活之后呈枉,項(xiàng)目中就會(huì)多出一個(gè) TodayWidget 的擴(kuò)展趁尼,新增的文件夾中的MainInterface.storyboard 和 TodayViewController 這個(gè)類就是我們要在通知中心顯示的界面的控制器檐什。storyborad,里面已經(jīng)有一個(gè)默認(rèn)的界面弱卡,其中只包含了一個(gè)label乃正,顯示“Hello World”。
TodayWidget擴(kuò)展都是以宿主App前綴開始的婶博。
我們先運(yùn)行項(xiàng)目瓮具,再運(yùn)行應(yīng)用擴(kuò)展。
這樣凡人,我門可以在系統(tǒng)的今日通知中心看到如下樣式
上述就完成了Today Extension的創(chuàng)建名党。
當(dāng)然你可以將擴(kuò)展中的 plist 中的 displayname 更換為宿主應(yīng)用名稱,在TodayViewController完成項(xiàng)目需要的UI挠轴。
共享數(shù)據(jù)
在 Today Extension 開發(fā)中传睹,避免不了要和宿主App之間共享數(shù)據(jù),比如岸晦,筆者的項(xiàng)目中需要使用項(xiàng)目中的域名欧啤、三方平臺(tái)請(qǐng)求頭部、服務(wù)器數(shù)據(jù)地址等等启上;
擴(kuò)展與宿主App之間共享數(shù)據(jù)有兩種方式:
- 通過NSUserDefaults
- 通過一個(gè)擴(kuò)展與App都可以訪問的共享容器邢隧,來存放文件,數(shù)據(jù)(Core Data冈在, Sqlite等都可以存放在這個(gè)共享的容器中)
首先倒慧,我們需要?jiǎng)?chuàng)建一個(gè) app group,如下圖包券,選中項(xiàng)目的Target -> Capabilities -> App Groups纫谅,打開,如果你以前創(chuàng)建過group溅固,會(huì)自動(dòng)列出來付秕。選擇+號(hào),填入group的名稱(記下這個(gè)名稱发魄,因?yàn)檫@個(gè)是擴(kuò)展和宿主之間共享數(shù)據(jù)的標(biāo)志符)
創(chuàng)建完成之后盹牧,選擇擴(kuò)展的Target -> Capabilities -> App Groups俩垃,打開励幼,選擇我們剛才所創(chuàng)建的group。
??: 如果出現(xiàn)了錯(cuò)誤口柳,應(yīng)該是名稱不可用苹粟,換一個(gè)重試
也可以登錄開發(fā)中選中《App Group》創(chuàng)建
在擴(kuò)展和宿主App打開group之后,項(xiàng)目中會(huì)多出兩個(gè)文件跃闹,如下圖
完成上述之后嵌削,我們利用剛剛的標(biāo)志符來存取共享的數(shù)據(jù)
// 存儲(chǔ)數(shù)據(jù)
[[[NSUserDefaults alloc] initWithSuiteName:@"group.com.LOLITA.appExtension"] setValue:myNote forKey:@"myShareData"];
// 取出數(shù)據(jù)
NSArray *myData = [[[NSUserDefaults alloc] initWithSuiteName:@"group.com.LOLITA.appExtension"] valueForKey:@"myShareData"];
這樣我們可以在擴(kuò)展和宿主App之間存取共享的數(shù)據(jù)了毛好。
補(bǔ)充:
如果需要存儲(chǔ)更多的數(shù)據(jù),可以通過文件或者數(shù)據(jù)庫(Core Data苛秕, Sqlite等)肌访。這個(gè)時(shí)候共享數(shù)據(jù)的方法就是要?jiǎng)?chuàng)建一個(gè)共享的文件夾。
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier: @"group.com.LOLITA.appExtension"];
通過上面的方法艇劫,擴(kuò)展和App就都可以訪問這個(gè)共享的文件夾了吼驶,將數(shù)據(jù)庫,文件等存儲(chǔ)在這個(gè)文件夾中店煞,也同樣的達(dá)到數(shù)據(jù)共享的目的蟹演。
使用宿主App中的文件
在擴(kuò)展中,總是避免不了想要使用宿主項(xiàng)目中的文件顷蟀,例如cell樣式酒请,數(shù)據(jù)處理工具等等,重寫一份當(dāng)然是可以的鸣个,但不是我們想要的結(jié)果羞反。
我們可以將需要用的文件也供用給擴(kuò)展,步驟如下
打開.m文件囤萤,選中下圖按鈕
這樣我們就可以在擴(kuò)展中使用該文件了
??:在選擇的文件中苟弛,如果包含了其他文件,一樣是需要添加到擴(kuò)展中的
擴(kuò)展中打開宿主App
既然擴(kuò)展作為了宿主App消息的展示欄阁将,肯定應(yīng)用的入口了膏秫,那么我們?cè)趺醋寯U(kuò)展和App之間進(jìn)行消息傳遞呢?例如做盅,我們需要打開某條消息的詳情缤削,或者是某個(gè)功能模塊。
我們知道吹榴,我們打開別的應(yīng)用是需要設(shè)置URL Types亭敢,然后通過URL Schemes來打開應(yīng)用的,同樣的图筹,擴(kuò)展也可以看成是其他應(yīng)用帅刀,這樣,我們勢(shì)必也要為自己的App設(shè)置一個(gè)URL Types远剩。
首頁我們?cè)O(shè)置一個(gè)URL Types
當(dāng)我們想通過openURL來打開應(yīng)用時(shí)扣溺,卻發(fā)現(xiàn)報(bào)錯(cuò)了
這是因?yàn)閿U(kuò)展不是一個(gè)完整的程序,所以它并沒沒有 sharedApplication
這個(gè)對(duì)象瓜晤。
所以Apple給每個(gè)UIViewController加了一個(gè) extensionContext
屬性锥余,在我們的宿主App中,這個(gè)屬性是nil痢掠,而在擴(kuò)展中驱犹,我們就可以通過extensionContext來執(zhí)行跳轉(zhuǎn)嘲恍。
我們?cè)邳c(diǎn)擊事件中添加如下代碼。
[self.extensionContext openURL:[NSURL URLWithString:@"AppExtension://add"] completionHandler:nil];
既然有跳轉(zhuǎn)雄驹,肯定涉及到傳處理了佃牛,我們?cè)贏ppDelegate里處理消息。
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
// 可以先回到應(yīng)用首頁医舆,在跳轉(zhuǎn)
if ([url.absoluteString hasPrefix:@"AppExtension"]) {
if ([url.absoluteString hasSuffix:@"add"]) {
// do something
}
else if ([url.absoluteString containsString:@"detail"]){
// do something
}
}
return YES;
}
補(bǔ)充
讀取xib文件吁脱、擴(kuò)展中支持三方框架、參數(shù)傳遞彬向、擴(kuò)展Widget高度兼贡、上架注意事項(xiàng)等**
- 讀取xib文件
如果cell樣式是xib,并出現(xiàn)讀取錯(cuò)誤問題娃胆,可以使用下面代碼嘗試遍希。
NSBundle *bundle = [NSBundle bundleForClass:[TodayItemView class]];
NSArray *cells = [bundle loadNibNamed:@"TodayItemView" owner:nil options:nil];
TodayItemView *itemView = cells.firstObject;
- 擴(kuò)展中支持三方框架
如果擴(kuò)展中使用到三方框架,則在Podfile中添加下面代碼里烦,并且update
target :'MyTodayWidget' do
platform :ios, '8.0'
pod 'AFNetworking', '~> 3.1.0'
end
- 參數(shù)傳遞
如果需要傳遞多個(gè)參數(shù)凿蒜,可以參考下面代碼嘗試
NSString *urlString = [NSString stringWithFormat:@"AppExtension://markCode=%@&code=%@&yesclose=%@&stockName=%@",market_stockCode,stockCode,preclose_px,[stockName stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
[self.extensionContext openURL:[NSURL URLWithString:urlString] completionHandler:nil];
??:url中不能出現(xiàn)中文,需要進(jìn)行UTF-8轉(zhuǎn)換胁黑,上面例子中废封,我將中文名稱進(jìn)行了轉(zhuǎn)換,你也可以將urlString整體進(jìn)行轉(zhuǎn)換丧蘸。
- url解析(接上面c)
如果url解析有問題漂洋,可以參考下面代碼嘗試
// 將url轉(zhuǎn)為http形式
NSString *tmpUrlString = [url.absoluteString stringByReplacingOccurrencesOfString:@"AppExtension://" withString:@"http://xxx?"];
NSURLComponents *components = [NSURLComponents componentsWithString:tmpUrlString];
NSArray* queryItems = components.queryItems;
NSMutableDictionary* queryItemDict = [NSMutableDictionary dictionary];
// 將value和name轉(zhuǎn)換為字典
for (NSURLQueryItem* item in queryItems) {
[queryItemDict setObject:item.value forKey:item.name];
}
- 擴(kuò)展Widget高度
系統(tǒng)默認(rèn)的高度為110,如果想要在通知中心擴(kuò)展高度力喷,可以使用下面代碼嘗試刽漂。
- (void)viewDidLoad {
[super viewDidLoad];
// 將小部件展現(xiàn)模型設(shè)置為可展開
self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
在代理方法中設(shè)置高度。
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
if (activeDisplayMode == NCWidgetDisplayModeExpanded) {
// 設(shè)置展開的新高度
self.preferredContentSize = CGSizeMake(0, NewHeight);
}else{
self.preferredContentSize = maxSize;
}
}
??:使用3DTouch喚出的彈窗依舊是110弟孟,上面代碼只是改變了通知中心的高度
- 上架注意事項(xiàng)
如果出現(xiàn)打包贝咙,或上架失敗,可以嘗試下面步驟拂募。
- 擴(kuò)展和target中的應(yīng)用包都選自動(dòng)管理簽名和證書
- 項(xiàng)目中配置正確的證書