有一個(gè) APP 里有若干個(gè) View Controller颠区,有些 Controller 里的 View 涉及到用戶敏感信息娜饵,比如顯示用戶存款金額和社保號(hào)瞻惋。程序進(jìn)入后臺(tái)后括袒,我需要在這類 Controller 上 present 一個(gè)模態(tài)的 View Controller 進(jìn)行遮擋(典型的用法就是彈出一個(gè)手勢(shì)密碼次兆,或者對(duì)這個(gè) Controller 上的 View 進(jìn)行高斯模糊),但是在其他不涉及用戶隱私的 Controller 上锹锰,我不能這樣做芥炭。那么在程序進(jìn)入后臺(tái)時(shí)漓库,我如何知道屏幕最上層的一個(gè)Controller 是什么鬼(拿到這個(gè) Controller 的實(shí)例),并選擇性的將其遮擋园蝠?
我去百度了這個(gè)問(wèn)題渺蒿,結(jié)果有不少答案在列。但是所有答案全是同一段類似的代碼:
//獲取當(dāng)前屏幕顯示的 View Controller
- (UIViewController *)getCurrentVC
{
UIViewController *result = nil;
UIWindow * window = [[UIApplication sharedApplication] keyWindow];
if (window.windowLevel != UIWindowLevelNormal) {
NSArray *windows = [[UIApplication sharedApplication] windows];
for(UIWindow * tmpWin in windows) {
if (tmpWin.windowLevel == UIWindowLevelNormal) {
window = tmpWin;
break;
}
}
}
UIView *frontView = [[window subviews] objectAtIndex:0];
id nextResponder = [frontView nextResponder];
if ([nextResponder isKindOfClass:[UIViewController class]])
result = nextResponder;
else
result = window.rootViewController;
return result;
}
分析了一下這段源碼彪薛,前半部分是獲取 APP 的普通層級(jí) window
茂装,我們的視圖一般會(huì)顯示在這個(gè) window 上。后部分源碼就有點(diǎn)不靠譜了善延,斷點(diǎn)打印[window subviews]
的值發(fā)現(xiàn)這個(gè)數(shù)組里只有一個(gè)元素:
這個(gè)UILayoutContainerView
就是 window 上的第一個(gè) subview少态,那么他的 nextResponder 自然是這個(gè) window ,所以結(jié)果總是返回rootViewController
挚冤。而 rootViewController 可能是 TabBarController 也可能是 NavigationController,假設(shè)我們的目標(biāo)控制器有著下面這種層級(jí)關(guān)系:
Window -> TabBarController -> NavigationController -> ViewController1 -> Target ViewController
上面這種方法只能拿到 TabBarController赞庶。
那么我們換一種思路训挡。若要獲取最上層的 view controller 可以采取【順藤摸瓜】模式,從 root view controller 開(kāi)始遞歸遍歷所有的控制器歧强,如果找到的控制器是 UITabBarController 的子類澜薄,就找它的selectedViewController
繼續(xù)向遞歸,如果找到的控制器是 UINavigationController 的子類摊册,就找它的visibleViewController
繼續(xù)遞歸肤京。這樣一路找上來(lái)總會(huì)到頭,最頭上的控制器就是我們的目標(biāo)控制器茅特。
這里我將其寫(xiě)成一個(gè)UIApplication
的分類方便使用:
@implementation UIApplication (ActivityViewController)
- (UIViewController *)activityViewController {
__block UIWindow *normalWindow = [self.delegate window];
if (normalWindow.windowLevel != UIWindowLevelNormal) {
[self.windows enumerateObjectsUsingBlock:^(__kindof UIWindow * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if (obj.windowLevel == UIWindowLevelNormal) {
normalWindow = obj;
*stop = YES;
}
}];
}
return [self p_nextTopForViewController:normalWindow.rootViewController];
}
- (UIViewController *)p_nextTopForViewController:(UIViewController *)inViewController {
while (inViewController.presentedViewController) {
inViewController = inViewController.presentedViewController;
}
if ([inViewController isKindOfClass:[UITabBarController class]]) {
UIViewController *selectedVC = [self p_nextTopForViewController:((UITabBarController *)inViewController).selectedViewController];
return selectedVC;
} else if ([inViewController isKindOfClass:[UINavigationController class]]) {
UIViewController *selectedVC = [self p_nextTopForViewController:((UINavigationController *)inViewController).visibleViewController];
return selectedVC;
} else {
return inViewController;
}
}
@end
在需要的時(shí)候直接使用[[UIApplication sharedApplication] activityViewController]
就可以返回最上層的 view controller 了:)忘分。
2017-02-06 更新
該方法太過(guò)繁瑣,可參考另一種方案白修。