iOS URLNavigator庫(kù)獲取的topMost錯(cuò)誤問(wèn)題解決,父子控制器的addChild正確添加與移除步驟

2025.01.31 工作變動(dòng)原因,故將一些工作期間Tapd內(nèi)部寫的Wiki文檔轉(zhuǎn)移到個(gè)人博客。

URLNavigator三方庫(kù) 跳轉(zhuǎn)失敗,經(jīng)過(guò)排查后發(fā)現(xiàn)獲取的 UIViewcontroller.navigationController 為空的問(wèn)題解決。

父子控制器的 addChild 正確添加與移除步驟。

一、問(wèn)題排查過(guò)程

問(wèn)題出現(xiàn)點(diǎn)源碼


tapd_44062861_1714122687_959.png
    open func push(_ viewController: UIViewController, from: UINavigationControllerType? = nil, animated: Bool = true) -> UIViewController? {
        guard (viewController is UINavigationController) == false else { return nil }
        // 使用斷點(diǎn)排查,發(fā)現(xiàn)在這里直接 { return nil }了,原因是獲取UIViewController.topMost?.navigationController的時(shí)候,navigationController為空,所以決定對(duì)topMost方法進(jìn)行排查。
        guard let navigationController = from ?? UIViewController.topMost?.navigationController else { return nil }
        guard self.delegate?.shouldPush(viewController: viewController, from: navigationController) != false else { return nil }
        navigationController.pushViewController(viewController, animated: animated)
        return viewController
      }

一路斷點(diǎn)排查,發(fā)現(xiàn)在 navigationController 這里直接 { return nil }了,原因是獲取 UIViewController.topMost?.navigationController 的時(shí)候,navigationController為空,所以決定對(duì)topMost方法進(jìn)行排查。

topMost方法

tapd_44062861_1714123000_598.png
        // child view controller
        for subview in viewController?.view?.subviews ?? [] {
          if let childViewController = subview.next as? UIViewController {
          // 最終調(diào)試發(fā)現(xiàn),三方庫(kù)獲取的childViewController并不是我正在顯示的控制器,而是拿的subView第一個(gè)子控制器(第一個(gè)添加的子控制器視圖),導(dǎo)致為空,所以要對(duì)子控制器進(jìn)行添加、移除管理解決。
            return self.topMost(of: childViewController)
          }
        }

最終調(diào)試發(fā)現(xiàn),三方庫(kù)獲取的 childViewController 并不是我正在顯示的控制器,而是 拿的subView第一個(gè)子控制器(第一個(gè)添加的子控制器視圖),導(dǎo)致為空。

因?yàn)槲?只對(duì)子控制器做了添加(addChild),然后使用 subView.ishidden來(lái)控制隱藏出現(xiàn)的問(wèn)題,所以要對(duì)子控制器進(jìn)行添加、移除管理解決。

二、父子控制器的addChild添加、移除正確管理

主控制器mainVC

// 在主控制器點(diǎn)擊 tab_A 切換子控制器
if title == "tab_A" {
    // 添加子控制器1
    let squareVC = UIViewController()
    // 將VC添加到控制器上,建立父子關(guān)系,這時(shí)可以通過(guò)`parentViewController`訪問(wèn)到父控制器;調(diào)用`addChildViewController`系統(tǒng)會(huì)自動(dòng)調(diào)用`willMoveToParentViewController:` 
    addChild(squareVC)
    // 將VC控制器的view添加到父控制器上
    view.addSubview(squareVC.view)
    // 調(diào)用VC的`didMoveToParentViewController`通知VC完成了父子關(guān)系建立。
    squareVC.didMove(toParent: self)
    
    // 移除子控制器2
    let myVC = UIViewController()
    // 通知子控制器即將解除父子關(guān)系
    myVC.willMove(toParent: nil)
    // 將VC的view從父控制器移除
    myVC.view.removeFromSuperview()
    // 通過(guò)`removeFromParentViewController`真正解除父子關(guān)系,并且系統(tǒng)會(huì)調(diào)用`didMoveToParentViewController:`
    myVC.removeFromParent()
}
// 在主控制器點(diǎn)擊 tab_B 切換子控制器
else if title == "tab_B" {
    // 添加子控制器2...
    
    // 移除子控制器1...
    
    // 原理同上
}

1. 子控制器添加:

  1. 將VC添加到控制器上,建立父子關(guān)系,這時(shí)可以通過(guò) parentViewController 訪問(wèn)到父控制器;調(diào)用 addChildViewController 系統(tǒng)會(huì)自動(dòng)調(diào)用 willMoveToParentViewController: 。
  2. 將VC控制器的view添加到父控制器上。
  3. 調(diào)用VC的 didMoveToParentViewController 通知VC完成了父子關(guān)系建立。

2. 子控制器移除:

  1. 通知子控制器即將解除父子關(guān)系。
  2. 將VC的view從父控制器移除。
  3. 通過(guò) removeFromParentViewController 真正解除父子關(guān)系,并且系統(tǒng)會(huì)調(diào)用 didMoveToParentViewController:

最終修改后具體實(shí)現(xiàn):

tapd_44062861_1714123000_599.png

最最最后,完結(jié)撒花

告辭.jpeg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容