摘要:runtime、kvo氨距、selectedIndex
在開發(fā)公司的一個(gè)項(xiàng)目時(shí)桑逝,遇到一個(gè)說正常也正常,說奇葩也奇葩的需求俏让,需求如下:1楞遏、首頁tab有好幾個(gè)其他tab的入口茬暇,點(diǎn)擊該入口進(jìn)入指定的tab(比如“秘籍"Tab)
2、進(jìn)入“秘籍”tab需要先登錄才能進(jìn)入使用寡喝,
2.1而钞、如果用戶已經(jīng)登錄,直接進(jìn)入“秘籍”tab使用相關(guān)功能
2.2拘荡、如果當(dāng)用戶未登錄時(shí)臼节,是需要先到“秘籍”tab所在頁面顯示空白提示信息,比如“您還未登錄珊皿,請(qǐng)先登錄”网缝,然后展示登錄頁面
2.2.1、如果正常登錄或注冊(cè)成功蟋定,顯示“秘籍”tab功能
2.2.2粉臊、如果用戶在登錄頁面沒有登錄,點(diǎn)擊了登錄頁面的返回按鈕驶兜,則要返回到“秘籍”之前的那個(gè)tab里扼仲,比如如果從首頁的某個(gè)按鈕進(jìn)入“秘籍”tab,則返回首頁抄淑,如果從“我的”tab點(diǎn)擊底部的tab按鈕就如“秘籍”tab屠凶,則回到“我的”tab
剛拿到這個(gè)需求時(shí)想著直接寫個(gè)自定義的UITabBarController,然后設(shè)置代理肆资,在shouldSelectViewController:和didSelectViewController:做下記錄進(jìn)入“秘籍”tab之前的tab矗愧,然后當(dāng)點(diǎn)擊登錄頁面返回按鈕時(shí)直接選中記錄的tab就可以了,但在實(shí)際的代碼里卻發(fā)現(xiàn)當(dāng)從首頁的按鈕入口進(jìn)入“秘籍”tab時(shí)郑原,使用的是tabVC.selectedIndex=xx,這種方式并不會(huì)走UITabBarController的代理回調(diào)唉韭,為了解決這個(gè)辦法,我寫了一個(gè)UITabBarController的分類犯犁,使用runtime修改了setSelectIndex:這個(gè)方法,然后為了方便起見属愤,也放棄了在shouldSelectViewController:和didSelectViewController:中操作的方式,因?yàn)樵摲椒梢酝瑫r(shí)讓我拿到old和new兩個(gè)tab對(duì)應(yīng)的內(nèi)容酸役,而使用kvo監(jiān)聽selectedViewController的變化住诸,但kvo也面臨使用setSelectIndex收不到通知的問題,解決辦法是在這個(gè)分類里手動(dòng)發(fā)送kvo的變化簇捍,具體分類代碼如下:
// ZQQTabBarController.h
@interface ZQQTabBarController :UITabBarController
@end
// ZQQTabBarController.m
@implementation ZQQTabBarController
- (void)viewDidLoad {
[self addObserver:selfforKeyPath:@"selectedViewController"options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOldcontext:nil];
}
- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context{
if([keyPathisEqualToString:@"selectedViewController"]) {
if(nil != change[@"old"]) {//記錄前一個(gè)tab}
}
}
// UITabBarController+ZQQTabBarController.h
@interface UITabBarController (ZQQTabBarController)
@end
// UITabBarController+ZQQTabBarController.m
@implementation UITabBarController (ZQQTabBarController)
+ (void)load
{
MethodoriginalMethod =class_getInstanceMethod(self,@selector(setSelectedIndex:));
MethodswizzledMethod =class_getInstanceMethod(self,@selector(zqq_setSelectedIndex:));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
- (void)zqq_setSelectedIndex:(NSInteger)index
{
if(index >=self.childViewControllers.count|| index <0) {
return;
}
if(self.delegate==nil) {//如果沒有代理只壳,直接調(diào)用修改index的方法
[self willChangeValueForKey:@"selectedViewController"];
[self zqq_setSelectedIndex:index];
[self didChangeValueForKey:@"selectedViewController"];
}else{
//即將被選中的controller
UIViewController *willBeSelectController =self.childViewControllers[index];
//如果可以被選中,則繼續(xù)下一步操作
if([self zqq_canSelectController:willBeSelectController]) {
//手動(dòng)發(fā)送節(jié)將修改通知
[self willChangeValueForKey:@"selectedViewController"];
//修改index
[self zqq_setSelectedIndex:index];
//如果delegate實(shí)現(xiàn)了這個(gè)代理方法暑塑,調(diào)用該代理方法
if([self.delegate respondsToSelector:@selector(tabBarController:didSelectViewController:)]) {
[self.delegate tabBarController:selfdidSelectViewController:willBeSelectController];
}
//發(fā)送已經(jīng)修改內(nèi)容通知
[self didChangeValueForKey:@"selectedViewController"];
}
}
}
/**
判斷即將被選中的controller是否可以被選中
@paramwillBeSelectController即將被選中的controller
@return是否可以被選中
*/
- (BOOL)zqq_canSelectController:(UIViewController*)willBeSelectController
{
//如果delegate實(shí)現(xiàn)了判斷是否可選中的方法吼句,用代理的方法判斷
if([self.delegate respondsToSelector:@selector(tabBarController:shouldSelectViewController:)]) {
return[self.delegate tabBarController:selfshouldSelectViewController:willBeSelectController];
}else{
//如果delegate沒有實(shí)現(xiàn)是否允許選中controller的方法,則默認(rèn)為可以選中
returnYES;
}
}
@end