學(xué)習(xí)前準(zhǔn)備工作:
一創(chuàng)建項(xiàng)目
聲明:本篇博客寫一半才發(fā)現(xiàn)widget單詞不小心寫錯(cuò)寫成了wedged。噓腰奋,大家知道就行了,懶得改項(xiàng)目名字再重新截圖了抱怔。
這里我就不多說(shuō)了劣坊,我這里創(chuàng)建一個(gè)wedgedDemo項(xiàng)目供學(xué)習(xí)使用。
我這里做的很簡(jiǎn)單屈留,一個(gè)主控制器包含一個(gè)一級(jí)界面和兩個(gè)二級(jí)界面局冰。
二創(chuàng)建appid和開發(fā)配置文件。
一般公司項(xiàng)目這些都已經(jīng)早就創(chuàng)建好了灌危,但這里為了給你們提供一個(gè)完整的演示康二,我就單獨(dú)創(chuàng)建為這個(gè)demo創(chuàng)建了。
準(zhǔn)備工作做完了勇蝙,下面正式進(jìn)入今天的主題:
一:添加Today Extension工程
在項(xiàng)目中創(chuàng)建一個(gè)target赠摇,具體為Xcode-->File-->New-->Target-->Today Extention
添加完成之后我們看到工程:
target工程命名一般我用:項(xiàng)目名+WidgetToday,這樣在項(xiàng)目中看文件區(qū)分很容易浅蚪,但是當(dāng)我們創(chuàng)建之后要去改一下Display Name,因?yàn)檫@個(gè)是我們widget里面要顯示的名字藕帜,這個(gè)根據(jù)項(xiàng)目需要去改,大家可以看看像美團(tuán)啊滴滴啊 這些應(yīng)用的widged就知道了惜傲。
如果不小心創(chuàng)建target了洽故,可以在這里選擇刪除重新創(chuàng)建
二:定制UI
憑個(gè)人習(xí)慣,默認(rèn)target會(huì)幫你創(chuàng)建一個(gè)storyboard盗誊。一般我習(xí)慣用純代碼繪制ui界面时甚,所以我會(huì)刪除默認(rèn)創(chuàng)建的MainInterface.storyboard,然后在info.plist中刪除NSExtensionMainStoryboard哈踱,添加NSExtensionPrincipalClass為TodayViewController
三:真機(jī)調(diào)試和打包配置
其實(shí)到這里我們已經(jīng)可以進(jìn)入開發(fā)工作了荒适,因?yàn)槟M器已經(jīng)可以跑起來(lái)了。但是在真機(jī)上卻不能調(diào)試开镣。我們把Today Extension當(dāng)作一個(gè)單獨(dú)的app刀诬,有自己的App ID和Provisioning profile. 所以,我們還需要注冊(cè)一個(gè)Today Extension的App ID和對(duì)應(yīng)的配置文件邪财。
Today Extension的bundle id一般命名方式為你的contain app的bundle id加上你創(chuàng)建的Today Extension工程名"com.jsc.demo.wedged"
將Today Extension的bundleID修改為剛剛為Today Extension創(chuàng)建的APP ID
打包或者真機(jī)調(diào)試的時(shí)候contain app與Today Extension選擇各自的profile文件就可以了陕壹。
注意點(diǎn):Today Extension版本號(hào)與contain app配置一致,否則審核上傳的時(shí)候會(huì)有警告树埠。
四:開發(fā)工作
打開我們的TodayViewController.m文件糠馆,我們可以進(jìn)入開發(fā)工作了
iOS 8
iOS8下沒有折疊和展開功能,默認(rèn)的Widget高度為self.preferredContentSize設(shè)置的高度怎憋。
self.preferredContentSize = CGSizeMake(kScreenW, 100);
iOS8下所有組件默認(rèn)右移30單位又碌,可以通過(guò)下面的方法修改上下左右的距離
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets {
return UIEdgeInsetsMake(0, 0, 0, 0);
}
iOS 10
iOS 10以后九昧,Widget有了兩種顯示模式
//高度固定,最低高度為110
NCWidgetDisplayModeCompact;
//折疊毕匀,高度可變
NCWidgetDisplayModeExpanded;
設(shè)定顯示模式耽装,需要在設(shè)定Size前設(shè)定這個(gè)屬性!F诰尽!
//設(shè)定顯示模式规个,需要在設(shè)定Size前設(shè)定這個(gè)屬性
if ([[UIDevice currentDevice] systemVersion].intValue >= 10) {
//高度固定凤薛,最低高度為110
// self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeCompact;
//折疊,高度可變
self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;
}
//貌似這個(gè)實(shí)在ios8下才有用诞仓,如果在ios10后缤苫,這個(gè)設(shè)置高度沒有用,系統(tǒng)固定高度墅拭,待測(cè)
self.preferredContentSize = CGSizeMake([UIScreen mainScreen].bounds.size.width, 200);
當(dāng)顯示模式設(shè)置為NCWidgetDisplayModeExpanded時(shí)活玲,點(diǎn)擊折疊和打開時(shí),會(huì)觸發(fā)下面這個(gè)方法谍婉,在這個(gè)方法中可以修改對(duì)應(yīng)狀態(tài)的高度
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
if (activeDisplayMode == NCWidgetDisplayModeCompact) {
self.preferredContentSize = CGSizeMake(maxSize.width, 110);
}
else {
self.preferredContentSize = CGSizeMake(maxSize.width, 200);
}
}
在下面的方法中更新視圖
-(void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// NCUpdateResultNewData 新的內(nèi)容需要重新繪制視圖
// NCUpdateResultNoData 部件不需要更新
// NCUpdateResultFailed 更新過(guò)程中發(fā)生錯(cuò)誤
completionHandler(NCUpdateResultNoData);
}
現(xiàn)在給大家看一下我的TodayViewController.h頁(yè)面舒憾,由于不想在一個(gè)個(gè)敲代碼,就直接上圖了
為了測(cè)試穗熬,我寫了一個(gè)button事件镀迂。
關(guān)于打開widged調(diào)用:
1.經(jīng)測(cè)試,每次手機(jī)左滑進(jìn)入widget唤蔗,都會(huì)調(diào)用widget頁(yè)面中的viewDidLoad和widgetPerformUpdateWithCompletionHandler方法探遵,其實(shí)左滑一次就是重新加載一次,但是存在間隔時(shí)間妓柜。(大概2秒左右間隔)箱季,不能一直左右滑動(dòng),這樣并不會(huì)調(diào)用方法棍掐。
2.我們的主程序app關(guān)閉之后藏雏,依然可以進(jìn)入button方法,也就是說(shuō)只要左滑出現(xiàn)widget作煌,那么這個(gè)target工程就一直在運(yùn)行了诉稍。側(cè)面說(shuō),左滑向當(dāng)于打開了我們的widget APP最疆。
五:代碼共享
在開發(fā)widged時(shí)杯巨,我們也需要用到主app中的對(duì)象,類以及一些三方庫(kù)努酸,那么我們?nèi)绾芜M(jìn)行代碼共享呢服爷?
我們先測(cè)一下,在widget中是否能直接#import主程序App某個(gè)類,然后使用呢仍源。
我們?cè)谥黜?xiàng)目中創(chuàng)建一個(gè)person類心褐,簡(jiǎn)單設(shè)置下屬性。然后在widget中使用試一下笼踩。
看樣子覺得可以用逗爹,xcode并沒有報(bào)錯(cuò),而且還幫你做了識(shí)別嚎于。但是一旦編譯掘而,就出錯(cuò)了。
找不到這個(gè)類喲于购,怎么辦袍睡,老實(shí)消停的往下看吧。
1:最簡(jiǎn)單最不推薦的方法肋僧,復(fù)制斑胜!我們可以將這些庫(kù)在Widget中復(fù)制一份。這樣嫌吠,我們的widged就可以用這些庫(kù)了止潘,但是安裝包會(huì)變大,程序運(yùn)行變慢辫诅,所以我不推薦覆山。
2:通過(guò)Pods導(dǎo)入,不太建議通過(guò)pod分別向兩個(gè)Target中導(dǎo)入第三方庫(kù)泥栖,因?yàn)楹苋菀装l(fā)生一些不好處理的問(wèn)題
3.將需要共享的文件按圖中進(jìn)行勾選配置簇宽,我也不建議,為啥呢吧享?一般項(xiàng)目中用的隨便一個(gè)三方庫(kù)魏割,比如AFNetWorking就有幾十個(gè)文件,而且我們用到的不止這些钢颂,你要勾到何年何月钞它,當(dāng)然你是在愿意,那也可以殊鞭,畢竟比前兩個(gè)方法好一些遭垛。
4.將公用代碼打包放在一個(gè)新的Framework中,然后link到主App和Widget中操灿,那就可以了锯仪。極力推薦這個(gè)。
怎么做呢趾盐?
第一步:和創(chuàng)建APP Extension一樣庶喜,New->Target小腊,選擇Cocoa Touch Framework來(lái)創(chuàng)建framework
第二步:我們?cè)赥oday Extension工程里面link這個(gè)新的PublicFramework
而在主app工程里面不用添加,因?yàn)閤code已經(jīng)幫我們自動(dòng)添加好了
第三步:將公用的類庫(kù)啊久窟,都拖到PublicFramework中
然后我們就可以使用啦
六:數(shù)據(jù)共享
1.首先需要去[蘋果開發(fā)者中心]的APP Groups中創(chuàng)建一個(gè)APP Group秩冈,命名方式"group.com.companyName.項(xiàng)目名",注意要以group開頭
2.編輯你的主 app的APP ID斥扛,Service中選中App Groups入问,并且點(diǎn)擊右邊的Edit按鈕選中剛剛創(chuàng)建的group,返回后稀颁,點(diǎn)擊Done完成APP ID的編輯芬失,再用同樣的方法編輯widget App ID。
3.此時(shí)我們的主 app和widget App中的Provisioning Profiles文件會(huì)顯示為無(wú)法使用峻村,我們需要重新編輯更新一下,并且下載下來(lái)覆蓋安裝锡凝。
4.Today Extension工程與app工程中的Capabilities配置都如下圖所示
通過(guò)App Groups提供的同一group內(nèi)app共同讀寫區(qū)域粘昨,可以用NSUserDefaults和NSFileManager兩種方式實(shí)現(xiàn)Today Extension和containing app之間的數(shù)據(jù)共享。
通過(guò)NSUserDefaults共享數(shù)據(jù)
- (void)saveDataByNSUserDefaults
{
NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.xxx.xxx"];
[shared setObject:@"test" forKey:@"widget"];
[shared synchronize];
}
- (NSString *)readDataFromNSUserDefaults
{
NSUserDefaults *shared = [[NSUserDefaults alloc] initWithSuiteName:@"group.com.xxx.xxx"];
NSString *value = [shared valueForKey:@"widget"];
return value;
}
通過(guò)NSFileManager共享數(shù)據(jù)
- (BOOL)saveDataByNSFileManager
{
//存在和Library同級(jí)目錄
NSError *error = nil;
NSFileManager *fm = [NSFileManager defaultManager];
//這里Identifier必須和你的group id保持一致窜锯,不然獲取的URL會(huì)為null
NSURL *groupURL = [fm containerURLForSecurityApplicationGroupIdentifier:@"group.come.jsc.jscdemo"];
NSURL *fileURL = [groupURL URLByAppendingPathComponent:@"appGroup.txt"];
NSLog(@"fileURL====%@",fileURL);
NSString *value = @"test";
//寫入文件
BOOL result = [value writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (!result) {
NSLog(@"%@",error);
} else {
NSLog(@"save value:%@ success.",value);
}
// //存在和Library/Caches文件下
// NSError *error = nil;
// NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.come.jsc.jscdemo"];
// containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];
//
// NSLog(@"containerURL====%@",containerURL);
// NSString *value = @"test222";
// BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&error];
// if (!result) {
// NSLog(@"%@",error);
// } else {
// NSLog(@"save value:%@ success.",value);
// }
return result;
}
- (NSString *)readDataByNSFileManager
{
NSError *error = nil;
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.come.jsc.jscdemo"];
NSURL *fileURL = [groupURL URLByAppendingPathComponent:@"appGroup.txt"];
NSString *value = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:&error];
return value;
// NSError *error = nil;
// NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.come.jsc.jscdemo"];
// containerURL = [containerURL URLByAppendingPathComponent:@"Library/Caches/test"];
// NSString *value = [NSString stringWithContentsOfURL:containerURL encoding:NSUTF8StringEncoding error:&error];
return value;
}
這樣就實(shí)現(xiàn)了Today Extension與app的數(shù)據(jù)共享
七:調(diào)起App
1.設(shè)置App的URLSchemes张肾,打開APP主要通過(guò)URLScheme打開和傳遞參數(shù)值。
設(shè)置URLSchemes時(shí)锚扎,要獨(dú)特一些吞瞪,避免與其他App重復(fù)
2.在Widget中添加點(diǎn)擊事件,用于觸發(fā)打開App的操作和傳遞參數(shù)驾孔。這里我在之前寫的button按鈕事件中添加打開App操作
//scheme為app的scheme
[self.extensionContext openURL:[NSURL URLWithString:@"widgedDemo://friends"]
completionHandler:^(BOOL success) {
NSLog(@"open url result:%d",success);
}];
// [self.extensionContext openURL:[NSURL URLWithString:@"widgedDemo://token=123456"]
// completionHandler:^(BOOL success) {
// NSLog(@"open url result:%d",success);
// }];
3.app的appdelegate的代理方法中芍秆,截取URL,做響應(yīng)處理:
// 所有版本的都可以使用
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[self appCallbackWithOpenUrl:url];
return YES;
}
/// iOS 8 以后
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
[self appCallbackWithOpenUrl:url];
return YES;
}
/// iOS 7
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
[self appCallbackWithOpenUrl:url];
return YES;
}
- (void)appCallbackWithOpenUrl:(NSURL *)url{
// 針對(duì)url進(jìn)行不同的操作
NSLog(@"url====%@",url);
NSLog(@"url.host===%@ url.scheme===%@",url.host,url.scheme);
if([url.absoluteString hasPrefix:@"widgedDemo"]) {
if([url.host isEqualToString:@"friends"]) {
//判斷是否是直接跳入到具體頁(yè)面
UINavigationController *nav = (UINavigationController *)self.window.rootViewController;
FriendsViewController *friendVC = [[FriendsViewController alloc] init];
[nav pushViewController:friendVC animated:YES];
}
}
}
有時(shí)候翠勉,我們發(fā)現(xiàn)運(yùn)行程序時(shí)妖啥,在app里面打斷點(diǎn),卻無(wú)法截取到url对碌,不會(huì)走那幾個(gè)方法荆虱。為什么呢?這是因?yàn)槟氵\(yùn)行的是 Today Extension 而不是我們的主app朽们,當(dāng)你創(chuàng)建了Today Extension時(shí)怀读,xcode自動(dòng)幫你默認(rèn)了運(yùn)行這個(gè)。當(dāng)然無(wú)法在appdelegate里面調(diào)試到斷點(diǎn)了骑脱。
將它改成我們的主app就可以了菜枷。記住,調(diào)試哪個(gè)叁丧,就切換到哪個(gè)犁跪。
以下借用別人已經(jīng)寫好的注意點(diǎn):
在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è)人翻譯,建議查看官方文檔
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)盡量按照以上方式避免被拒絕枫耳。
widget開發(fā)注意點(diǎn):
widget開發(fā)注意點(diǎn):
widget開發(fā)注意點(diǎn):
重要的事情說(shuō)三遍7Ψ!迁杨!
1.取資源圖片取不到钻心?當(dāng)前taget---build phases--copy bundle resources看看是不是沒有?有可能你兩個(gè)taget來(lái)回移動(dòng)或者別的操作铅协,導(dǎo)致當(dāng)前taget已經(jīng)失去了這個(gè)圖片資源捷沸。添加資源一定要看看是給那個(gè)target用還是兩個(gè)都要用
還有資源文件,如果我們用mainBundle取資源文件,每個(gè)target的mainbundle應(yīng)該也是不同的狐史,這個(gè)我還沒有測(cè)痒给,有機(jī)會(huì)大家可以取測(cè)試一下。
2.運(yùn)行項(xiàng)目不走調(diào)試骏全?一定要搞清楚我們?cè)谡{(diào)試那個(gè)target苍柏,如果是widget,就run widget target姜贡,如果是demo項(xiàng)目试吁,就run demo target
3.一定要記得共享代碼,共享代碼楼咳,別把類放在其中一個(gè)target下熄捍,用另一個(gè)target去調(diào)用
好了,附上我的代碼Demo地址
4.widget有一個(gè)自己的沙盒母怜,app有一個(gè)自己的沙盒
5.關(guān)于PublicFramework治唤。關(guān)系系統(tǒng)的.framework屬于動(dòng)態(tài)庫(kù),而我們創(chuàng)建的.framework屬于動(dòng)態(tài)庫(kù)糙申。如果直接創(chuàng)建宾添,然后link使用真機(jī)模擬器都是沒有問(wèn)題,可是一旦archive時(shí)就找不到PublicFramework中的文件了柜裸。為什么缕陕?因?yàn)槲覀冞€少了一些配置。鬼知道我經(jīng)歷了什么后才寫下來(lái)第5點(diǎn)注意點(diǎn)8硗Α?敢亍!直接看別人的博客連接吧铐然,關(guān)于動(dòng)態(tài)庫(kù)制作的蔬崩。https://blog.csdn.net/u014361280/article/details/80938740
還有恶座,如果我們?cè)陟o態(tài)庫(kù)中添加了libsqlite3.tbd這類的庫(kù),運(yùn)行時(shí)會(huì)報(bào)錯(cuò):libsqlite3.tbd is not an object file (not allowed in a library).
關(guān)于這個(gè)解釋繼續(xù)看別人的博客吧沥阳,心態(tài)崩了跨琳,不想寫了:
https://blog.csdn.net/sandy_shell/article/details/49247045
總之,記住制作靜態(tài)庫(kù)后一定要配置桐罕,看博客https://blog.csdn.net/u014361280/article/details/80938740的第4點(diǎn)脉让、更改Xcode項(xiàng)目配置,如果我們的靜態(tài)庫(kù)中添加了libsqlite3.tbd這類庫(kù)功炮,直接刪了溅潜,在項(xiàng)目中添加就行了,靜態(tài)庫(kù)直接刪了薪伏。
你以為到這里滚澜,這個(gè)靜態(tài)庫(kù)就不給你找事了?嫁怀?设捐?
WTF!打包時(shí)又出問(wèn)題眶掌!懶得說(shuō)問(wèn)題了挡育,上博客:http://www.reibang.com/p/8e28914f013e
到這里可以打包了巴碗,但是朴爬!項(xiàng)目出問(wèn)題了,上博客:http://www.reibang.com/p/d9fd65b5f727
上圖片:
總之橡淆,只要在app中和widget開發(fā)中召噩,我們用到了第三方的靜態(tài)庫(kù)(.a文件),那么在各自的build setting中找到other link flags逸爵,檢查-licucore具滴、-ObjC 和-all_load是否添加。具體為什么师倔,可以看這個(gè)博客:https://blog.csdn.net/cjh965063777/article/details/51880966
至此完成构韵!樓主心態(tài)已崩!再見G魉摇F;帧!
NSUserDefaults *dft = [NSUserDefaults standardUserDefaults];
[dft setObject:@"widget" forKey:@"widget"];
[dft synchronize];
如果用standardUserDefaults瓷胧,則widget里面用存在widget沙盒目錄里显拳,app用則存放在沙盒目錄里。如果放在PublicFramework中搓萧,做成公共方法的話杂数,則誰(shuí)調(diào)用此方法宛畦,會(huì)存在誰(shuí)的沙盒里面。[NSBundle mainBundle]也是如此揍移,說(shuō)明本身PublicFramework雖然是一個(gè)target次和,但它的類型導(dǎo)致它并沒有自己的沙盒。
5.公共庫(kù)PublicFramework里面的文件找不到:
問(wèn)題:如果我們直接在PublicFramework里面創(chuàng)建類羊精,或者從外部拖入斯够,是沒有問(wèn)題的。app和App Extensions都能使用喧锦,可是如果我們從項(xiàng)目app中把一些三方庫(kù)或者自己封裝的類庫(kù)拖進(jìn)去读规,會(huì)發(fā)現(xiàn)App Extensions找不到這個(gè)文件罩旋,無(wú)法調(diào)用里面的方法修械。
解決方案:
首先看我們自己創(chuàng)建的類庫(kù):
這里沒問(wèn)題,是正常link到PublicFramework中叔磷,而app和App Extensions又link了PublicFramework阵具,所以沒問(wèn)題碍遍。
在看我們從app中已有的類庫(kù)拖入到PublicFramework中:
注意到,我們的nssring分類阳液,xcode并沒有幫你更改怕敬。還是link到app中,因此app extensions并不能使用這個(gè)分類帘皿。這可能和xcode版本有關(guān)东跪,也可能和項(xiàng)目有關(guān),有時(shí)候xcode也會(huì)幫你自動(dòng)改好鹰溜,也有時(shí)候不會(huì)改虽填。總之曹动,當(dāng)我們拖入類庫(kù)到PublicFramework中斋日,一定要檢查這個(gè)link。
另外墓陈,當(dāng)我們創(chuàng)建類庫(kù)時(shí)恶守,大多數(shù)忽略一個(gè)選項(xiàng):
這里又target讓我們選,只不過(guò)xcode幫我們自動(dòng)做了勾選贡必,你在哪個(gè)target下創(chuàng)建的兔港,默認(rèn)幫你勾選了這個(gè)。