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

Posted

技术标签:

【中文标题】Swift 3 - 以编程方式创建标题栏约束【英文标题】:Swift 3 - Create title bar constraints programmatically 【发布时间】:2017-02-24 23:34:45 【问题描述】:

我正在使用 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)

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

但在水平视图中:

设置的正确约束是什么,以便它占据栏的整个宽度,并且由于状态栏在水平时消失,因此顶部没有多余的空间?

编辑

在提出@thexande 建议后,我确​​实收到了一个错误:

[LayoutConstraints] 视图层次结构没有为 约束:<NSLayoutConstraint:0x608000098100 UILabel:0x7fe35b60edc0'Scan Results'.left == UIView:0x7fe35b405c20.left (inactive)> 添加到视图时, 约束的项目必须是该视图的后代(或视图 本身)。如果之前需要解决约束,这将崩溃 视图层次结构已组装。打断 -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] 进行调试。 2017-02-24 21:01:59.807 EOB-Reader[78109:10751346] * 中的断言失败 -[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] * 终止应用 由于未捕获的异常“NSInternalInconsistencyException”,原因: '不可能在未准备好视图层次结构的情况下设置布局 约束。'

我还更新了原始帖子中的代码。

【问题讨论】:

【参考方案1】:

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

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 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)
    

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

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

【讨论】:

这是一个很大的帮助。谢谢你。我做了您建议的更改,但我收到了一个错误,包含在我编辑的原始帖子中。 抱歉中断。我听取了您的建议,将其提取到一个单独的 ViewController 类中,然后我对其进行了实例化。感谢您的所有帮助!

以上是关于Swift 3 - 以编程方式创建标题栏约束的主要内容,如果未能解决你的问题,请参考以下文章

Swift iOS以编程方式在导航栏下方设置scrollView约束

StackView 中的按钮约束(以 Swift 编程方式)

如何以编程方式创建标签栏控制器后添加导航界面(Swift)

在 rootView Swift 3.0 之后以编程方式在 ViewControllers 添加栏按钮项

Swift:以编程方式嵌入标签栏控制器后,导航栏消失

swift Swift - 以编程方式添加导航栏标题和按钮