storyboard可謂Xcode神器涂邀,比xib不知道高到哪里去了,也是apple一直推薦使用的侍芝。但是隨之而來(lái)的是兩個(gè)問題:一是研铆,多人開發(fā)時(shí)需要同時(shí)修改storyboard很容易導(dǎo)致文件沖突,二是州叠,storyboard文件里控制器太多棵红,電腦打開時(shí)特別卡和慢。我想這兩個(gè)問題是導(dǎo)致目前很多開發(fā)者選擇xib而不是storyboard的原因把咧栗。所以在iOS9以后出現(xiàn)了 Storyboard Reference逆甜。有點(diǎn)跑題。
一個(gè)解決辦法是致板,根據(jù)項(xiàng)目功能的劃分交煞,建立多個(gè)storyboard,各個(gè)模塊互不影響斟或。那么如何從許多個(gè)storyboard文件中快速取出我想要的那個(gè)控制器呢素征?下面應(yīng)該是大家使用的方法
UIStoryboard *sb = [UIStoryboard storyboardWithName:storyboardName bundle:[NSBundle mainBundle]];
UIViewController *vc = [sb instantiateViewControllerWithIdentifier:identifier];
關(guān)鍵是,storyboardName和identifier如果不對(duì),將會(huì)拋出異常稚茅。而且,app內(nèi)部那么多跳轉(zhuǎn)平斩,老是寫這樣的代碼亚享,不覺得煩么?
本文討論的是如何從多個(gè)storyboard中取出控制器實(shí)例绘面。不用管控制在哪個(gè)storyboard文件里欺税,只要 控制器設(shè)置identifier為類名就OK。
使用UIViewController的類別方法
#import <UIKit/UIKit.h>
@interface UIViewController (Storyboard)
+ (nullable instancetype)instanceFromStoryboardV2;
@end
使用 instanceFromStoryboardV2 取出以調(diào)用者類名為identifier的實(shí)例即可揭璃。如果沒有取到晚凿,返回nil
YouViewController *vc = [YouViewController instanceFromStoryboardV2];
具體實(shí)現(xiàn)步驟:
首先:檢查緩存里面有沒有保存這個(gè)identifier對(duì)應(yīng)的storyboard名字。如果有緩存瘦馍,直接從這個(gè)storyboard里面取
接著:獲取NSBunble的storyboard文件列表歼秽。篩選出storyboard文件名
第三:遍歷這個(gè)列表,嘗試取出實(shí)例情组。
最后:獲得實(shí)例后對(duì)storyboard名進(jìn)行緩存燥筷,同時(shí)返回實(shí)例
+ (nullable instancetype)instanceFromStoryboardV2
{
NSString *identifier = NSStringFromClass([self class]);
// 取緩存的storyboard名
NSCache *cache = [self cache];
NSString *cacheStoryboardName = [cache objectForKey:identifier];
if (cacheStoryboardName) {
return [self tryTakeOutInstanceFromStoryboardNamed:cacheStoryboardName identifier:identifier];
}
// 未緩存,遍歷storyboard文件名列表院崇,開始嘗試取出實(shí)例肆氓。
for (NSString *name in [self storyboardList]) {
UIViewController *instance = [self tryTakeOutInstanceFromStoryboardNamed:name identifier:identifier];
if (instance) {
// 成功獲取實(shí)例后,對(duì)storyboard名進(jìn)行緩存
[cache setObject:name forKey:identifier];
return instance;
}
}
return nil;
}
取出項(xiàng)目的storyboard文件列表
storyboard在NSBunble中是以storyboardc為后綴的底瓣。所以只要從NSBunble中查找所有storyboardc的文件就可以啦谢揪。
需要注意的是,xcode會(huì)額外自動(dòng)生成一個(gè)帶 ~iPhone 和 ~iPad 的storyboards文件捐凭。我們只需要storyboard文件名拨扶,所以這兩種文件我們需要忽略掉。
+ (nonnull NSArray*)storyboardList
{
static NSArray *kBundleStoryboardNameList;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSMutableArray *tmp = [NSMutableArray array];
/**
* 找到所有storyboard文件茁肠。
* @warning 會(huì)忽略帶有 ~iphone(iPhone應(yīng)用)或 ~ipad(ipad應(yīng)用)標(biāo)志的 storyboard文件名
*/
NSArray *list = [NSBundle pathsForResourcesOfType:@"storyboardc" inDirectory:[NSBundle mainBundle].resourcePath];
for (NSString *path in list) {
NSString *ext = [path lastPathComponent];
NSString *name = [ext stringByDeletingPathExtension];
if ([name rangeOfString:@"~"].location == NSNotFound) {
[tmp addObject:name];
}
}
kBundleStoryboardNameList = [NSArray arrayWithArray:tmp];
});
return kBundleStoryboardNameList;
}
嘗試取出實(shí)例
UIStoryboard的+storyboardWithName: bundle:方法如果name不正確屈雄,會(huì)拋出異常
-instantiateViewControllerWithIdentifier: 方法如果identifier在當(dāng)前UIStoryboard找不到,也會(huì)拋出異常官套。如果不做處理酒奶,會(huì)導(dǎo)致app崩潰。
所以這里采用了 try catch 對(duì)異常進(jìn)行捕獲奶赔。拋出異常時(shí)惋嚎,直接返回nil。
+ (nullable instancetype)tryTakeOutInstanceFromStoryboardNamed:(nonnull NSString *)storyboardName identifier:(nonnull NSString *)identifier
{
if (!storyboardName || !identifier) {
return nil;
}
@try {
UIStoryboard *sb = [UIStoryboard storyboardWithName:storyboardName bundle:[NSBundle mainBundle]];
UIViewController *vc = [sb instantiateViewControllerWithIdentifier:identifier];
return vc;
}
@catch (NSException *exception) {
return nil;
}
@finally {
}
}
緩存
+ (NSCache *)cache
{
static NSCache *cache;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
cache = [[NSCache alloc] init];
});
return cache;
}