ios swift present 多个页面时 报错 或 崩溃

603 阅读2分钟

问题 当present 多个页面时报错 或崩溃

Attempt to present <LZPresentDemo.FirstDetailViewController: 0x10ab0b350> on <UITabBarController: 0x10e037000> (from <LZPresentDemo.FirstViewController: 0x10aa152a0>) which is already presenting <LZPresentDemo.SecondDetailViewController: 0x10ab1caf0>.

image.png

有说明可知 ,当有present页面时 不能再次present,看视图逻辑
也就是 ① navB2 present B2 后,不能 再次 navB2 prensent A1.
② navB2 present B2 后,不能 再次 navA1 prensent A1.

image.png

怎么解决问题呢?

答案:就是由最顶端的 ViewController 再次Prensent 我们想要的ViewController
这下就很清楚了:使用present去弹模态视图的时候,只能用最顶层的的控制器去弹,用底层的控制器去弹会失败,并抛出警告
比如 navB2 present B2 后,再有B2 prensent A1.解决问题。

image.png

多次弹出的视图层次

image.png

nav.vcB2 present B2 的 各数据

示例

let secondDetailVC = SecondDetailViewController()
        secondDetailVC.modalPresentationStyle = .custom
        
        print("弹出 secondDetailVC = \(secondDetailVC)  title  = \(secondDetailVC.title) \n")
        
        self.present(secondDetailVC, animated: true) { [weak self] in
            guard let self = self else { return }
            
            print("弹出 secondDetailVC , nav.presentedViewController =  \(self.navigationController?.presentedViewController),nav.presentingViewController = \(self.navigationController?.presentingViewController)")
            
            print("弹出 secondDetailVC , nav.vc.presentedViewController =  \(self.presentedViewController),nav.vc.presentingViewController = \(self.presentingViewController)")
            
            print("弹出 secondDetailVC , secondDetailVC.presentedViewController =  \(secondDetailVC.presentedViewController),secondDetailVC.presentingViewController = \(secondDetailVC.presentingViewController); title = \(secondDetailVC.title)")
            
            if let sceneDelegate = UIApplication.shared.connectedScenes
                .first?.delegate as? SceneDelegate
            {
                // 你现在可以使用sceneDelegate来访问Scene相关的方法和属性
                let tabbarVC = sceneDelegate.tabbarVC
                
                let vcA1 =  sceneDelegate.navVCA1
                let navA1 = vcA1?.navigationController
                
                print("弹出 secondDetailVC , navA1.presentedViewController =  \(navA1?.presentedViewController),nav.presentingViewController = \(navA1?.presentingViewController)")
                print("弹出 secondDetailVC , vcA1.presentedViewController =  \(vcA1?.presentedViewController),vcA1.presentingViewController = \(vcA1?.presentingViewController)")
                
                
                print("弹出 secondDetailVC , tabbarVC.presentedViewController =  \(tabbarVC?.presentedViewController),tabbarVC.presentingViewController = \(tabbarVC?.presentingViewController) \n")
            }
        }
        

image.png

规律

nav.vcB2 present B2
B2 的 presentingViewController = UITabBarController 重点
tabbarVC的 presentedViewController = navB2.vc.presentedViewController = navB2.presentedViewController = navA1.vc.presentedViewController = navA1.presentedViewController = B2

注意tabbar 意想不到
tabbar 里的 nav, 和 nav 里的vc presentedViewController 都是 B2 更意想不到

获取最上方的ViewController

我简单地写了个方法来获取传入viewController的最顶层子节点,大家可以参考下

 //获取最顶层的弹出视图,没有子节点则返回本身 注意:当有多个nav present 多个vc 时,此方法不使用
    static func topestPresentedViewControllerForVC(viewController: UIViewController) -> UIViewController{
        var topestVc = viewController
        while let topVC = topestVc.presentedViewController {
            topestVc = topVC
        }
        return topestVc
    }
    
    //获取最顶层的弹出视图,没有子节点则返回本身
    static func topestPresentedViewControllerForVC(tabViewController: UITabBarController) -> UIViewController{
        var topestVc: UIViewController = tabViewController
        while let topVC = topestVc.presentedViewController {
            topestVc = topVC
        }
        return topestVc
    }

demo 地址 github.com/lizhi0123/L…

感谢评论区 解决方案

1.webp