Swift 5中作为控制器根视图的UIView获取UIResponder始终为nil问题
问题根因
该问题核心是响应链构建时机不匹配:
- 当你在
UIViewController的viewDidLoad方法中直接将自定义Home实例赋值给view属性时,此时Home实例刚完成初始化,还没有被挂载到完整的响应链上,next属性还没被系统设置为对应的控制器实例,此时调用parent()方法自然拿到nil。 - 子视图调用相同逻辑可以正常获取控制器,是因为子视图是在
Home已经完成响应链挂载之后才被添加到视图层级的,此时响应链已经完整,遍历可以正常命中控制器。
另外你提供的parent()方法代码存在编译问题,返回值声明为非可选UIViewController却在最后返回nil,需要修改为可选返回类型UIViewController?才能正常编译。
可行解决方案
方案1:调整方法调用时机
将获取父控制器的逻辑延迟到视图已经完成父视图挂载之后执行,在Home类中重写didMoveToSuperview方法,确认视图已经被添加到层级后再执行对应逻辑:
class Home: UIView { override func didMoveToSuperview() { super.didMoveToSuperview() // 只有当前视图已经被添加到父视图时才执行逻辑,避免移除时触发无效调用 guard superview != nil else { return } let parentController = self.parent() // 此处执行需要用到控制器的逻辑 } func parent() -> UIViewController? { var responder: UIResponder? = self.next while let currentResponder = responder { if let vc = currentResponder as? UIViewController { return vc } responder = currentResponder.next } return nil } }
方案2:依赖注入控制器(更稳定高效)
如果需要在初始化阶段就拿到控制器实例,直接给自定义Home类添加弱引用的控制器属性,初始化时显式传入,无需遍历响应链,完全规避时机问题:
// Home类定义 class Home: UIView { weak var parentVC: UIViewController? init(parentVC: UIViewController) { self.parentVC = parentVC super.init(frame: .zero) // 初始化阶段即可直接使用parentVC } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } // ViewController中调用 class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view = Home(parentVC: self) } }
内容的提问来源于stack exchange,提问作者hanabi_noir
相关产品推荐
相关产品推荐

