UIWebView场景下present UIImagePickerController触发视图层级警告求助
嘿,我之前也踩过这个一模一样的坑!这个问题的核心就是视图层级不匹配——当你尝试弹出UIImagePickerController时,你用来present它的那个UIViewController,它的视图已经不在当前窗口的视图层级里了,系统找不到正确的父容器来承载相册选择器,就会触发那个警告,甚至顺带把当前VC给强制dismiss掉。
先拆解一下警告的含义
Warning: Attempt to present
whose view is not in the window hierarchy
这句话直白点说就是:你想把相册选择器展示在某个UIViewController上,但这个控制器的视图根本不在当前App的窗口层级里,系统没法完成present操作,只能抛出警告并做异常处理(比如dismiss当前VC)。
具体解决方案
我当时试了几个办法,最有效的是下面这些:
用当前App最顶层的控制器来present相册选择器
不要直接用WebView所在的VC来present,而是动态获取当前窗口中处于最上层的控制器。可以写个工具方法来实现:func getTopMostViewController() -> UIViewController? { guard let keyWindow = UIApplication.shared.connectedScenes .filter({$0.activationState == .foregroundActive}) .compactMap({$0 as? UIWindowScene}) .first?.windows .filter({$0.isKeyWindow}).first else { return nil } var topVC = keyWindow.rootViewController while let presentedVC = topVC?.presentedViewController { topVC = presentedVC } return topVC }然后在弹出相册选择器的时候,用这个方法拿到的控制器来present:
let imagePicker = UIImagePickerController() imagePicker.sourceType = .photoLibrary imagePicker.delegate = self // 用顶层控制器弹出 getTopMostViewController()?.present(imagePicker, animated: true, completion: nil)(如果是OC项目,也可以写对应的Objective-C版本的工具方法,核心逻辑都是找到最上层的presented控制器)
检查WebView相关的视图层级逻辑
你可以排查下这几个点:- 当前WebView所在的VC是不是被present出来的?有没有在某个回调(比如WebView的
didFinishNavigation)里不小心调用了dismiss? - 你的VC是不是被嵌入在
UINavigationController或UITabBarController中?如果是,不要直接用VC本身,而是确认导航控制器或标签栏控制器是否在视图层级里。
- 当前WebView所在的VC是不是被present出来的?有没有在某个回调(比如WebView的
验证JS与原生交互的时机
如果是通过WebView里的JS触发弹出相册,那要确保原生接收事件时,当前的VC视图确实还在窗口中。可以在触发present前加个判断:if self.view.window != nil { // 视图在层级里,可以正常present } else { // 视图不在层级里,改用顶层控制器 }
内容的提问来源于stack exchange,提问作者Abhinandan Pratap

