嵌套的控制器+视图,以编程方式添加约束,在哪里放置?最佳实践?

时间:2018-09-11 19:38:59

标签: ios constraints swift4 nslayoutconstraint

关于布局限制/锚点最佳做法,我有多个问题……

摘要

  1. 我想了解最佳实践以及可能的一些解释,在何处实例化引用父视图部分的约束。我看到viewDidLoad不好
  2. viewDidLoad的哪些替代方法有意义
  3. 还有放置它的地方或设置什么,因此它会在旋转更新中被调用(某些常数值是基于给定宽度和元素数量的复杂计算,因此我需要重新计算)
  4. 我是否需要将所有约束保存在属性中并在更新时引用它们,还是可以一遍又一遍地调用self.view.leadingAnchor.constraint(equalTo: parentController.view.leadingAnchor,[..])

我的设置

在我的代码中,我有一个这样的嵌套层次结构

rootController
view
    menuViewController
    view
        menuOneViewController
        view
            UIButton1
            UIButton2
            ...
            UIButton6

每个级别在其父级别的锚点方面都有限制。

我首先在每个Controller的viewDidLoad方法中添加约束。

menuViewController:

override func viewDidLoad() {
    super.viewDidLoad()

    self.menuOneViewController = MenuOneViewController()
    guard let menuOneViewController = self.menuOneViewController else { return }

    menuOneViewController.parentController = self

    self.view.addSubview(menuOneViewController.view)
}

menuOneViewController:

override func viewDidLoad() {
    super.viewDidLoad()
    guard let parentController = self.parentController else { return }
    let menuRowHeight = parentController.menuRowHeight

    self.view.heightAnchor.constraint(equalToConstant: menuRowHeight).isActive = true
    self.view.leadingAnchor.constraint(equalTo: parentController.view.leadingAnchor, constant: 0).isActive = true
    self.view.trailingAnchor.constraint(equalTo: parentController.view.trailingAnchor, constant: 0).isActive = true
    self.view.topAnchor.constraint(equalTo: parentController.view.topAnchor, constant: -(menuRowHeight)).isActive = true

通过此设置,我收到以下消息:

'Unable to activate constraint with anchors 
<NSLayoutXAxisAnchor:0x600001199900 "UIView:0x7fd99e705b90.leading">
and <NSLayoutXAxisAnchor:0x600001199980 "UIView:0x7fd99e526bc0.leading"> 
because they have no common ancestor.  Does the constraint or its 
anchors reference items in different view hierarchies?  That's illegal.'

我的想法和详细信息

我想我明白了……我想在viewDidLoad方法中还没有建立层次结构。我猜想它发生在viewDidLoad()之后,所以我要么需要将这些约束移到viewDidLoad之后调用的子方法中,要么可以在addSubview调用之后在父方法中创建约束。

现在,尽管后一种方法肯定可以工作,但我仍然想知道我通常会把它放在哪个地方。而且,我也不想让子视图控制器视图应该有多大的逻辑困扰父视图控制器。

那我可以将这样的代码放在updateViewConstraints中吗?因为如果这样的话,轮换似乎没有调用它,这是我接下来要关心的问题,还是我需要设置一些标志或执行某项操作。手动触发。如果是这样,哪种方法?在什么级别?

如果该方法将被调用两次,由于重复的约束生成/重复的约束冲突,我是否会遇到问题?在这种情况下,最佳做法是什么?

在这方面,如果我使用NSLayoutConstraint(item: view, [..]).isActive = trueview.anchor.constraint(

的话,是否有任何关系?

我首先了解了NSLayoutConstraint,然后了解了其他方式(锚和可视格式语言),现在我混合了anchor.constraints和NSLayoutConstraint,不确定该走哪条路。但是,由于我喜欢干净的代码,所以我想坚持一种方法。

2 个答案:

答案 0 :(得分:1)

首先要添加子级VC

addChildViewController(child) 
view.addSubview(child.view)
child.view.translatesAutoresizingMaskIntoConstraints = false
// set constraints here
child.didMove(toParentViewController: self)

第二 viewDidLoad是最好的选择,因为旋转会在设备旋转时自动调整自身的布局,但是如果您要进行调整,请将其设置为var约束,在内部

进行调整
func viewWillTransition(to size: CGSize, 
               with coordinator: UIViewControllerTransitionCoordinator) {}

Apple 也建议使用锚点,因为它会自动将约束添加到适当的父级,并使用NSLayoutConstraint.activate([-,-,-,])激活许多约束,而不是在每个约束上都放置.active = true

答案 1 :(得分:1)

问题可能是在将viewDidLoad()的视图添加到menuOneViewController的视图之前调用了menuViewController。这就是为什么您遇到...because they have no common ancestor运行时错误的原因。

更大的问题和解决方案是,孩子不应该知道该对象受其约束的视图。这不仅会降低组件menuOneViewController的可重用性,还会使调试变得更加困难在将来。在将menuViewController的视图添加为子视图之后,应在mainOneViewController中设置约束。另外,您可以使用NSLayoutConstraint.activate(_:)和不使用constant:参数的.constraint(equalTo:)来节省一些空间。

这是viewDidLoad()的{​​{1}}替代:

menuViewController