子视图控制器的视图忽略自动布局约束

Posted

技术标签:

【中文标题】子视图控制器的视图忽略自动布局约束【英文标题】:Child view controller's view ignores Auto Layout constraints 【发布时间】:2020-07-10 17:42:18 【问题描述】:

我想将子视图控制器添加到UINavigationController's 视图。我有这样的层次结构 NavigationController -> View(drawer) -> View(contentContainer) -> 子视图控制器应该固定到 contentContainer

但由于某种原因,它忽略了约束,我得到了奇怪的结果。请看截图,drawer 是绿色的,contentContainer 是黄色的,子控制器几乎放置在屏幕之外,并且框架高度为 0。

关键代码在 drawerContentController didSetsetupView 方法中。请注意,我使用 SnapKit 进行约束,但通过传统方式设置 NSLayoutConstraints 会发生同样的问题

class NavigationController: UINavigationController 
    private var drawerHeightConstraint: Constraint!
    
    fileprivate lazy var drawer = UIView()
    private lazy var contentContainer = UIView()
    var drawerContentController: UIViewController? 
        didSet 
            guard let new = drawerContentController else 
                oldValue?.removeFromParent(animated: true)
                return
            
            if let old = oldValue 
                old.removeFromParent(animated: true)  _ in
                    self.add(child: new, superview: self.contentContainer, animated: true, completion: nil)
                
             else 
                add(child: new, superview: contentContainer, animated: true, completion: nil)
            
        
    
    
    override func viewDidLoad() 
        super.viewDidLoad()
        setupView()
    
    
    private func setupView() 
        view.addSubview(drawer)
        drawer.backgroundColor = .green
        drawer.snp.makeConstraints  make in
            make.leading.bottom.trailing.equalToSuperview()
            drawerHeightConstraint = make.height.equalTo(360).constraint
        
        
        let blur = UIBlurEffect(style: .default)
        let visualView = UIVisualEffectView(effect: blur)
        drawer.addSubview(visualView)
        visualView.pinToSuperView()
        
        contentContainer.backgroundColor = .yellow
        drawer.addSubview(contentContainer)
        contentContainer.snp.makeConstraints  make in
            make.edges.equalTo(drawer.safeAreaLayoutGuide)
        
    

    func add(child controller: UIViewController, superview: UIView? = nil, animated: Bool, completion: ((Bool) -> Void)? = nil) 
        
        controller.willMove(toParent: self)
        addChild(controller)
        controller.view.alpha = 0
        (superview ?? view).addSubview(controller.view)
        controller.view.pinToSuperView()
        
        UIView.animate(withDuration: animated ? 0.3 : 0, animations: 
            controller.view.alpha = 1
        )  finished in
            controller.didMove(toParent: self)
            completion?(finished)
        
    


extension UIView 
    func pinToSuperView() 
        snp.makeConstraints  make in
            make.edges.equalToSuperview()
        
    

当我向 contentContainer 添加一个简单的 UIView 时,它会按预期工作

var drawerContentController: UIViewController? 
    didSet 
        let v = UIView()
        v.backgroundColor = .purple
        contentContainer.addSubview(v)
        v.pinToSuperView()
    

【问题讨论】:

看到感叹号了吗?这些是您正在犯的自动布局错误。你需要看看它们是什么。 它只是说视图的大小不明确。您是否看到在第二个屏幕截图中,在彩色视图下可以看到表格视图?虽然第一个根本没有控制器,但 UINavigationController 似乎不喜欢向其中添加子控制器。我会将导航控制器嵌入到其他控制器中,并将子视图添加到嵌入控制器中 我不知道你所说的“公正”是什么意思。歧义您遇到问题的原因。 它不是,正如我在帖子中所说,使用相同约束添加和定位的简单 UIView 可以正常工作。 UINavigationController 似乎不允许添加子视图控制器 【参考方案1】:

好的,我“解决”了这个问题。

似乎UINavigationController 不喜欢添加子视图控制器。如屏幕截图所示,当添加子控制器时,导航控制器的整个视图层次结构都被破坏了。在第二个屏幕截图中,表格视图控制器(白色视图)正确显示,但在第一种情况下,它甚至不在视图层次结构中!

所以道德是,尽量不要将子视图控制器直接添加到UINavigationController,最终它不是一个正常的UIViewController,甚至没有一个正常的UIView。我添加了另一个UIViewController 作为UINavigationController 的容器,并将子控制器添加到其中。它现在按预期工作。

【讨论】:

一个 UINavigationController 确实有一个“普通的 UIView”。这里的问题仅仅是你没有考虑到 UINavigationController 已经 有孩子的事实,它以适合导航控制器的特殊方式对待它们:它们是推送到其堆栈上的视图控制器,简单明了。

以上是关于子视图控制器的视图忽略自动布局约束的主要内容,如果未能解决你的问题,请参考以下文章

iOS:在 iOS 7 和 iOS 8 上将约束应用于子视图的自动布局差异

垂直自动布局子视图

忽略自动布局约束,然后恢复它们

在所有子视图自动布局之前调用 viewWillTransitionToSize

自动布局约束没有得到尊重

IOS6 使用自动布局的约束