iOS URLNavigator库获取的topMost错误问题解决,父子控制器的addChild正确添加与移除步骤

210 阅读3分钟

2025.01.31 工作变动原因,故将一些工作期间Tapd内部写的Wiki文档转移到个人博客。

URLNavigator三方库 跳转失败,经过排查后发现获取的 UIViewcontroller.navigationController 为空的问题解决。

父子控制器的 addChild 正确添加与移除步骤。

一、问题排查过程

问题出现点源码 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 }
    	// 使用断点排查,发现在这里直接 { return nil }了,原因是获取UIViewController.topMost?.navigationController的时候,navigationController为空,所以决定对topMost方法进行排查。
        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
      }

一路断点排查,发现在 navigationController 这里直接 { return nil }了,原因是获取 UIViewController.topMost?.navigationController 的时候,navigationController为空,所以决定对topMost方法进行排查

topMost方法

tapd_44062861_1714123000_598.png

        // child view controller
        for subview in viewController?.view?.subviews ?? [] {
          if let childViewController = subview.next as? UIViewController {
    	  // 最终调试发现,三方库获取的childViewController并不是我正在显示的控制器,而是拿的subView第一个子控制器(第一个添加的子控制器视图),导致为空,所以要对子控制器进行添加、移除管理解决。
            return self.topMost(of: childViewController)
          }
        }

最终调试发现,三方库获取的 childViewController 并不是我正在显示的控制器,而是 拿的subView第一个子控制器(第一个添加的子控制器视图),导致为空。

因为我 只对子控制器做了添加(addChild),然后使用 subView.ishidden来控制隐藏出现的问题,所以要对子控制器进行添加、移除管理解决。

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

主控制器mainVC

// 在主控制器点击 tab_A 切换子控制器
if title == "tab_A" {
    // 添加子控制器1
    let squareVC = UIViewController()
	// 将VC添加到控制器上,建立父子关系,这时可以通过`parentViewController`访问到父控制器;调用`addChildViewController`系统会自动调用`willMoveToParentViewController:` 
    addChild(squareVC)
	// 将VC控制器的view添加到父控制器上
    view.addSubview(squareVC.view)
	// 调用VC的`didMoveToParentViewController`通知VC完成了父子关系建立。
    squareVC.didMove(toParent: self)
	
    // 移除子控制器2
	let myVC = UIViewController()
	// 通知子控制器即将解除父子关系
    myVC.willMove(toParent: nil)
	// 将VC的view从父控制器移除
    myVC.view.removeFromSuperview()
	// 通过`removeFromParentViewController`真正解除父子关系,并且系统会调用`didMoveToParentViewController:`
    myVC.removeFromParent()
}
// 在主控制器点击 tab_B 切换子控制器
else if title == "tab_B" {
    // 添加子控制器2...
	
    // 移除子控制器1...
	
	// 原理同上
}

1. 子控制器添加:

  1. 将VC添加到控制器上,建立父子关系,这时可以通过 parentViewController 访问到父控制器;调用 addChildViewController 系统会自动调用 willMoveToParentViewController:
  2. 将VC控制器的view添加到父控制器上。
  3. 调用VC的 didMoveToParentViewController 通知VC完成了父子关系建立。

2. 子控制器移除:

  1. 通知子控制器即将解除父子关系。
  2. 将VC的view从父控制器移除。
  3. 通过 removeFromParentViewController 真正解除父子关系,并且系统会调用 didMoveToParentViewController:

最终修改后具体实现:

企业微信截图_20e3a67c-88eb-4832-a650-67cb269e2c34.png

最最最后,完结撒花

告辞.jpeg