(下面内容参考自 参考:从一个需求问题看iOS的事件处理)
前两天遇到的一个需求是封装一个SDK,在某个API调用的时候需要知道应用当前展现在屏幕最前层对应的Controller对象。最终大概的方案是这样的:
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;
这其中有Window和Responder的概念,这些都是和iOS事件处理相关的内容,下面基于这个代码示例解释和整理一下。
前半部分是“找Window”,后半部分是“在Window中找View和对应的Controller”。
在iOS中,window这个概念对应于UIWindow类,主要负责展示视图和做事件分发处理。而UIWindow对象中有一个属性叫做windowLevel,标志着这个window在显示和事件处理方面的层级,我们正常运行中看到的window是默认的UIWindowLevelNormal,对应为0,此外还定义了两个级别的常量,对应statusBar和alert。而这里我们要找的,就是UIWindowLevelNormal这个层面的window对象。
Window既然是展现视图的,那么也就要从view找起,通过index为0的UIView向上找,直到“响应链”上的一个ViewController。
Responder链
在上面那段示例代码中,可以看到,后面寻找特定Controller的过程实际上就是根据nextResponder属性进行迭代。这个nextResponder实际上是UIResponder类的一个方法,返回的引用也是一个UIResponder类对象。
UIResponder是什么?它可以是一个UIView(包括UIControl和UIWindow)、UIViewController,甚至可以是一个UIApplication。
看UIResponder类,它提供了很多功能,而其中最主要的自然是负责响应事件。
而Responder链的路径如下图所示(引自苹果官方文档):
此外,关于事件对象的封装和事件处理的完整流程,可参见原文: