UINavigationController 与子视图控制器的奇怪行为

Posted

技术标签:

【中文标题】UINavigationController 与子视图控制器的奇怪行为【英文标题】:Strange behavior of UINavigationController with child view controller 【发布时间】:2018-07-15 16:16:45 【问题描述】:

我需要导航控制器堆栈中所有控制器的可拖动视图。为了管理这个视图和手势识别器,我使用了名为 ChildViewController 的自定义 VC。在 navController 的 viewDidLoad 方法中,我添加了子控制器。在 ChildViewController 中,我在 didMoveToParent 方法中创建视图并设置约束。在手势识别器中使用约束的常数来计算平移时的视图位置。及其作品。但是每次当我在 navController 的堆栈中推送或弹出控制器时,navController 都会调用 ChildViewController 的 didMoveToParent 方法并设置约束。之后,手势识别器开始无法正常工作。我通过实现 isConstrainted 变量并在 didMoveToParent 中添加 if 语句来解决这个问题。现在,约束设置一劳永逸。但是,当我在viewDidLoad中添加一次子vc时,为什么navController在推送或弹出时调用子vc的didMoveToParent? GIF https://i.stack.imgur.com/MEeu6.gif gitHub 上的项目https://github.com/ayatsev/ChildNavController

class CustomNavController: UINavigationController 

    override func viewDidLoad() 
        super.viewDidLoad()
        let childController = ChildViewController()
        addChild(childController)
        view.addSubview(childController.view)
        childController.didMove(toParent: self)
    




class ChildViewController: UIViewController 

var xConstraint: NSLayoutConstraint!
var yConstraint: NSLayoutConstraint!
var isConstrainted = false


lazy var childView: UIView = 
    let cv = UIView()
    cv.translatesAutoresizingMaskIntoConstraints = false
    let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(handlePan))
    cv.addGestureRecognizer(panRecognizer)
    cv.backgroundColor = .green
    return cv
()

override func viewDidLoad() 
    super.viewDidLoad()
    view = childView 


override func didMove(toParent parent: UIViewController?) 
    print("--- CHILD: Did Move To Parent")
    guard let parent = parent else return
    if !isConstrainted 
        childView.widthAnchor.constraint(equalToConstant: 50).isActive = true
        childView.heightAnchor.constraint(equalToConstant: 50).isActive = true
        xConstraint = childView.leadingAnchor.constraint(equalTo: parent.view.leadingAnchor, constant: 40)
        xConstraint.isActive = true
        yConstraint = childView.topAnchor.constraint(equalTo: parent.view.topAnchor, constant: parent.view.frame.height * 2 / 3)
        yConstraint.isActive = true
        isConstrainted = true
    


@objc func handlePan(recognizer: UIPanGestureRecognizer) 
    guard let parent = parent else return
    let translation = recognizer.translation(in: parent.view)

    switch recognizer.state 
    case .changed:
        xConstraint.constant += translation.x
        yConstraint.constant += translation.y
        recognizer.setTranslation(CGPoint.zero, in: parent.view)
    default:
        break
    




【问题讨论】:

【参考方案1】:

但是为什么navController在push或者pop的时候会调用child vc的didMoveToParent

没有必要问这个“为什么”。这就是系统的工作方式。有时didMove(toParent:) 到达时没有对应的willMove(toParent:)。有时didMove(toParent:) 会到达,即使此视图控制器以前是此父级的子级并且仍然是此父级的子级。这似乎很疯狂,我知道。但是你不要去解释为什么,你的编码方式应该能够连贯地响应你收到的信息。

【讨论】:

我相信我的回答是错误的。我现在相信你得到didMove(toParent:) 的原因是因为你对didMove(toParent:) 的实现是错误的。它需要首先调用super,即它应该是super.didMove(toParent:parent)

以上是关于UINavigationController 与子视图控制器的奇怪行为的主要内容,如果未能解决你的问题,请参考以下文章

使用 I2C 来与子设备通信

加入与子查询

父 ViewModel 与子通信

父窗口与子窗口的层次关系

与子记录一起删除记录

eclipse 包与子包的视图显示方式切换