Swift 3 - 以编程方式创建标题栏约束

Swift 3 - Create title bar constraints programmatically

我正在使用 Swift 3,iOS 10,XCode 8.2。

在我的代码中,我需要以编程方式创建一个 UIViewController,因此,也以编程方式指定其布局和内容。

@IBAction func testViewController() {

    let detailViewController = UIViewController()
    detailViewController.view.backgroundColor = UIColor.white

    let titleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints =  false
        label.backgroundColor = UIColor.blue
        label.text = "Scan Results"
        label.textAlignment = .center
        label.font = UIFont.boldSystemFont(ofSize: 18)
        label.textColor = UIColor.white
        return label
    }()

    let titleConstraints: [NSLayoutConstraint] = [
        NSLayoutConstraint(item: titleLabel, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0),
        NSLayoutConstraint(item: titleLabel, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0),
        NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0),
        NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 40)
    ]

    detailViewController.view.addSubview(titleLabel)

    detailViewController.view.addConstraints(titleConstraints)

    self.navigationController?.pushViewController(detailViewController, animated: true)
}

在垂直视图中(忽略所有其他垃圾;只关注蓝色标题栏):

但在水平视图中:

什么是正确的约束设置,以便它占据栏的整个宽度并且没有顶部额外的 space 因为状态栏在水平时消失?

编辑

在提出@thexande 建议后,我确​​实收到错误消息:

[LayoutConstraints] The view hierarchy is not prepared for the constraint: <NSLayoutConstraint:0x608000098100 UILabel:0x7fe35b60edc0'Scan Results'.left == UIView:0x7fe35b405c20.left (inactive)> When added to a view, the constraint's items must be descendants of that view (or the view itself). This will crash if the constraint needs to be resolved before the view hierarchy is assembled. Break on -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] to debug. 2017-02-24 21:01:59.807 EOB-Reader[78109:10751346] * Assertion failure in -[UIView _layoutEngine_didAddLayoutConstraint:roundingAdjustment:mutuallyExclusiveConstraints:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3600.6.21/NSLayoutConstraint_UIKitAdditions.m:649 2017-02-24 21:01:59.951 EOB-Reader[78109:10751346] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Impossible to set up layout with view hierarchy unprepared for constraint.'

我也更新了原来的代码post。

发生这种情况的原因是因为您正在使用框架。您根据屏幕的宽度计算了框架。你不需要框架,你可以使用自动布局来完成这一切。相反,您应该使用约束将标签固定到它的超级视图边界,并给它一个静态高度。例如:

lazy var titleConstraints: [NSLayoutConstraint] = [
    NSLayoutConstraint(item: self.titleLabel, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0),
    NSLayoutConstraint(item: self.titleLabel, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0),
    NSLayoutConstraint(item: self.titleLabel, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0),
    NSLayoutConstraint(item: self.titleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 40)
]

然后,在viewDidLoad()

self.view.addConstraints(titleConstraints)

您可以像这样简化您的标签声明。不要忘记自动调整大小掩码标志以使约束正常工作:

let titleLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints =  false
    label.backgroundColor = UIColor.blue
    label.text = "Scan Results"
    label.textAlignment = .center
    label.textColor = UIColor.white
    return label
}()

最后,您正在做奇怪的数学运算以使视图控制器的顶部紧靠导航栏控制器的底部。删除所有垃圾并将以下内容放入 viewDidLoad() 以使视图控制器的顶部紧靠 UINavigationBar 的底部:

self.edgesForExtendedLayout = []

更新:

这里的问题是您将视图和约束附加到尚未分配的视图控制器中。

我们在 viewDidLoad() 中附加子视图和约束的原因是因为我们不能在视图之前添加子视图和约束....did....加载到内存中。否则,它不存在,你会得到上面的错误。考虑将 detailViewController 分解为 class 声明,如下所示:

class detailViewController: UIViewController {
let eobTitleLabel: UILabel = {
    let label = UILabel()
    label.translatesAutoresizingMaskIntoConstraints =  false
    label.backgroundColor = UIColor.blue
    label.text = "Scan Results"
    label.textAlignment = .center
    label.font = UIFont.boldSystemFont(ofSize: 18)
    label.textColor = UIColor.white
    return label
}()

    lazy var eobTitleConstraints: [NSLayoutConstraint] = [
       NSLayoutConstraint(item: self.eobTitleLabel, attribute: .left, relatedBy: .equal, toItem: self.view, attribute: .left, multiplier: 1, constant: 0),
        NSLayoutConstraint(item: self.eobTitleLabel, attribute: .right, relatedBy: .equal, toItem: self.view, attribute: .right, multiplier: 1, constant: 0),
        NSLayoutConstraint(item: self.eobTitleLabel, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0),
        NSLayoutConstraint(item: self.eobTitleLabel, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 40)
    ]

    override func viewDidLoad() {
        self.view.addSubview(eobTitleLabel)
        self.view.addConstraints(self.eobTitleConstraints)
    }
}

另外,不要冒犯他人,但您的代码有点乱。以后应该避免的事情:

  1. 正在向不存在的标签添加约束。 (重命名修复约束的标签)
  2. 您正在出口方法中声明变量。不要这样做,在 class 级别声明方法和属性。
  3. 了解 OOP 及其在 swift 中的实现方式。这将帮助您了解完成任务的方法和模式:)