嵌套控制器+视图,以编程方式添加约束,哪个地方?最佳实践?
Nested controllers + views, programmatically adding constraints, which place ? best practice?
我有多个关于布局的问题 constraints/anchor 最佳实践...
总结
- 我想了解最佳实践和一些解释,在哪里实例化引用父视图部分的约束。我看到 viewDidLoad 不好
- viewDidLoad 的哪些替代方案有意义
- 但是还有放在哪里,或者设置什么,所以它在旋转更新时被调用(一些常量值是基于给定宽度和元素数量的复杂计算,然后我需要重新计算)
- 我是否需要将所有约束保存在属性中并在更新时引用它们,或者我可以一遍又一遍地调用
self.view.leadingAnchor.constraint(equalTo: parentController.view.leadingAnchor,[..])
我的设置
在我的代码中,我有一个像这样的嵌套层次结构
rootController
view
menuViewController
view
menuOneViewController
view
UIButton1
UIButton2
...
UIButton6
每个级别对其父级别的锚都有限制。
我开始在每个控制器的 viewDidLoad 方法中添加约束。
菜单视图控制器:
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 = true
与 view.anchor.constraint(
有什么关系吗?
我首先了解了 NSLayoutConstraint,然后了解了其他方式(锚点和视觉格式语言),现在我混合使用了 anchor.constraints 和 NSLayoutConstraint,不知道该走哪条路。但是因为我喜欢我的代码干净,所以我想坚持使用一种方式。
首先添加一个child VC你应该
addChildViewController(child)
view.addSubview(child.view)
child.view.translatesAutoresizingMaskIntoConstraints = false
// set constraints here
child.didMove(toParentViewController: self)
Second viewDidLoad
是最好的地方,关于旋转 auto-layout 会在设备旋转时自行调整,但如果你想调整一些东西将其作为 var 约束并在
内进行调整
func viewWillTransition(to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator) {}
此外 Apple 建议使用锚点,因为它会自动将约束添加到适当的 parent ,加上使用 NSLayoutConstraint.activate([-,-,-,])
激活许多约束而不是将 .active = true
每一个
问题可能是 viewDidLoad()
在 menuOneViewController
的视图添加到 menuViewController
的视图之前被调用。这就是您收到 ...because they have no common ancestor
运行时错误的原因。
更大的问题和解决方案是 child 不应该知道它被约束的视图。这不仅会降低组件的可重用性 menuOneViewController
,而且还会使以后的调试更加困难。将 mainOneViewController
的视图添加为子视图后,应在 menuViewController
中设置约束。此外,使用不带 constant:
参数的 NSLayoutConstraint.activate(_:)
, as well as .constraint(equalTo:)
可以节省一些 space。
这是 menuViewController
的 viewDidLoad()
覆盖:
override func viewDidLoad() {
super.viewDidLoad()
self.menuOneViewController = MenuOneViewController()
guard let menuOneViewController = self.menuOneViewController else { return }
menuOneViewController.parentController = self
self.view.addSubview(menuOneViewController.view)
let subView = menuOneViewController.view
NSLayoutConstraint.activate(constraints: [
subView.heightAnchor.constraint(equalToConstant: menuRowHeight),
subView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor)
subView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)
subView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: -menuRowHeight)
]
}
我有多个关于布局的问题 constraints/anchor 最佳实践...
总结
- 我想了解最佳实践和一些解释,在哪里实例化引用父视图部分的约束。我看到 viewDidLoad 不好
- viewDidLoad 的哪些替代方案有意义
- 但是还有放在哪里,或者设置什么,所以它在旋转更新时被调用(一些常量值是基于给定宽度和元素数量的复杂计算,然后我需要重新计算)
- 我是否需要将所有约束保存在属性中并在更新时引用它们,或者我可以一遍又一遍地调用
self.view.leadingAnchor.constraint(equalTo: parentController.view.leadingAnchor,[..])
我的设置
在我的代码中,我有一个像这样的嵌套层次结构
rootController
view
menuViewController
view
menuOneViewController
view
UIButton1
UIButton2
...
UIButton6
每个级别对其父级别的锚都有限制。
我开始在每个控制器的 viewDidLoad 方法中添加约束。
菜单视图控制器:
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 = true
与 view.anchor.constraint(
我首先了解了 NSLayoutConstraint,然后了解了其他方式(锚点和视觉格式语言),现在我混合使用了 anchor.constraints 和 NSLayoutConstraint,不知道该走哪条路。但是因为我喜欢我的代码干净,所以我想坚持使用一种方式。
首先添加一个child VC你应该
addChildViewController(child)
view.addSubview(child.view)
child.view.translatesAutoresizingMaskIntoConstraints = false
// set constraints here
child.didMove(toParentViewController: self)
Second viewDidLoad
是最好的地方,关于旋转 auto-layout 会在设备旋转时自行调整,但如果你想调整一些东西将其作为 var 约束并在
func viewWillTransition(to size: CGSize,
with coordinator: UIViewControllerTransitionCoordinator) {}
此外 Apple 建议使用锚点,因为它会自动将约束添加到适当的 parent ,加上使用 NSLayoutConstraint.activate([-,-,-,])
激活许多约束而不是将 .active = true
每一个
问题可能是 viewDidLoad()
在 menuOneViewController
的视图添加到 menuViewController
的视图之前被调用。这就是您收到 ...because they have no common ancestor
运行时错误的原因。
更大的问题和解决方案是 child 不应该知道它被约束的视图。这不仅会降低组件的可重用性 menuOneViewController
,而且还会使以后的调试更加困难。将 mainOneViewController
的视图添加为子视图后,应在 menuViewController
中设置约束。此外,使用不带 constant:
参数的 NSLayoutConstraint.activate(_:)
, as well as .constraint(equalTo:)
可以节省一些 space。
这是 menuViewController
的 viewDidLoad()
覆盖:
override func viewDidLoad() {
super.viewDidLoad()
self.menuOneViewController = MenuOneViewController()
guard let menuOneViewController = self.menuOneViewController else { return }
menuOneViewController.parentController = self
self.view.addSubview(menuOneViewController.view)
let subView = menuOneViewController.view
NSLayoutConstraint.activate(constraints: [
subView.heightAnchor.constraint(equalToConstant: menuRowHeight),
subView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor)
subView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)
subView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: -menuRowHeight)
]
}