百度中有一大把获取当前UIViewController的代码,但是在ios8一旦present之后就拿不到了,在百度找了一大推都是没用的东西,后来翻墙找老外,有老外发现了这个问题,但是给出的解决方案也不尽人意,最后笔者在实际解决中一次偶然机会发现了这个问题。
首先是ios7下面的代码:
//获取当前屏幕显示的viewcontroller
- (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;
}
IOS8下面获取当前VC代码如下(兼容ios7):
+(UIViewController*)getCurrentKeyController
{
UIViewController *result;
UIWindow *topWindow = [[[UIApplication sharedApplication] delegate] window];
NSLog(@"%@",[[UIApplication sharedApplication] windows]);
if (topWindow.windowLevel != UIWindowLevelNormal)
{
NSArray *windows = [[UIApplication sharedApplication] windows];
for(topWindow in windows)
{
if (topWindow.windowLevel == UIWindowLevelNormal)
break;
}
}
id lenderClass = objc_getClass("UILayoutContainerView"); // 通过字符串名字,获取类
id nextResponder;
UIView *rootView = [[topWindow subviews] objectAtIndex:0];
if(IsIOS8&&![rootView isMemberOfClass:[lenderClass class]])
{
NSArray *arr = [rootView valueForKey:@"subviewCache"];
if(arr.count>0)
{
UIView *v = [arr objectAtIndex:0];
nextResponder = [v nextResponder];
}
else
{
nextResponder = [[[rootView subviews] objectAtIndex:0] nextResponder];
}
}
else
{
nextResponder = [rootView nextResponder];
}
if ([nextResponder isKindOfClass:[UIViewController class]])
{
result = nextResponder;
}
else if ([topWindow respondsToSelector:@selector(rootViewController)] && topWindow.rootViewController != nil)
{
result = topWindow.rootViewController;
}
else
{
NSAssert(NO, @"ShareKit: Could not find a root view controller. You can assign one manually by calling [[SHK currentHelper] setRootViewController:YOURROOTVIEWCONTROLLER].");
}
return result;
}
比起直接分享代码,笔者更愿意跟大家分享我解决这个问题的过程和思路
ios7下面 window 的subview最上面一层一定是UILayoutContainerView 这可以让我捕获到viewcontroller ios8下面是一个过度的UITransitionView 捕获不到VC
这里面比较复杂 我那天搞了一个下午 我猜测苹果的用意应该是不允许今后随意捕捉用户界面 给用户一个干净的体验环境。UILayoutContainerView(容器view)这个在api只有class 看不到任何接口~ UITransitionView(过度view)这个连api都进不去。
- 在IOS7下UIApplication的Window的subView的第一个view一定是UILayoutContainerView,而它的nextResponder就是一个ViewController,这是为什么能给通过Window找到ViewController的原因。在ios8中,一旦使用了presentViewController,而presentViewController的UIApplication的Window的subView的第一个view就变成了UITransitionView,它的nextResponder还是一个Window,这样看起来似乎有一种Window与UITransitionView循环的情况,笔者也不清楚苹果是怎么做到的。
- 后来我发现,在IOS8之下,其实Window的UILayoutContainerView被偷偷藏在了Window的subView的第一个view的一个叫做subviewCache的数组里面,于是我利用Runtime获取了这个subviewCache数组里面的UILayoutContainerView,问题就解决了。这就是上面一段代码
`id lenderClass = objc_getClass("UILayoutContainerView"); // 通过字符串名字,获取类
id nextResponder;
UIView *rootView = [[topWindow subviews] objectAtIndex:0];
if(IsIOS8&&![rootView isMemberOfClass:[lenderClass class]])
{
NSArray *arr = [rootView valueForKey:@"subviewCache"];
if(arr.count>0)
{
UIView *v = [arr objectAtIndex:0];
nextResponder = [v nextResponder];
}
else
{
nextResponder = [[[rootView subviews] objectAtIndex:0] nextResponder];
}
}
加了一个判断的原因。
[欢迎读者指出不足之处,转载请注明出处。]