以编程方式添加前导/顶部约束

Posted

技术标签:

【中文标题】以编程方式添加前导/顶部约束【英文标题】:adding leading / top constraints programmatically 【发布时间】:2019-03-07 19:45:23 【问题描述】:

我有以下代码适用于高度和宽度限制。尝试添加前导约束和顶部约束时崩溃。我还有其他具有高度、宽度、前导、顶部约束的按钮集,但它们都设置在情节提要上,所以我认为这是我必须添加到每个按钮的唯一 4 个约束。

我有 7 个按钮,每个按钮对应一周中的一天。当我添加代码来执行前导和顶部约束时,它会因下面的错误代码而中断。它只适用于高度/宽度。我猜这与我添加子视图的方式或按钮与视图控制器的关系有关,或者在以编程方式执行时,我需要的不仅仅是我一直在使用的 4 个约束(前导、顶部、宽度、高度)在故事板上。

func setupWeekdayButtons() 
    self.weeklyButtons = [self.mondayButton, self.tuesdayButton, self.wednesdayButton, self.thursdayButton, self.fridayButton, self.saturdayButton, self.sundayButton]


    for i in 0...6 
        print(i)

        self.weeklyButtons[i].translatesAutoresizingMaskIntoConstraints = false
        self.weeklyButtons[i].setTitle(self.weekdayLabels[i], for: .normal)
        self.weeklyButtons[i].layer.borderColor = UIColor.black.cgColor
        self.weeklyButtons[i].titleLabel?.textAlignment = .center
        self.weeklyButtons[i].layer.borderWidth = 1.0
        self.weeklyButtons[i].layer.cornerRadius = 6.0
        self.weeklyButtons[i].setTitleColor(.black, for: .normal)
        self.weeklyButtons[i].setTitleColor(.red, for: .selected)
        self.weeklyButtons[i].addTarget(self, action: #selector(selectedDailyButton), for: .touchUpInside)
        self.view.addSubview(self.weeklyButtons[i])

        let heightConstraint = NSLayoutConstraint(
            item: self.weeklyButtons[i],
            attribute: NSLayoutConstraint.Attribute.height,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: nil,
            attribute: NSLayoutConstraint.Attribute.notAnAttribute,
            multiplier: 1.0,
            constant: 30
        )
        let widthConstraint = NSLayoutConstraint(
            item: self.weeklyButtons[i],
            attribute: NSLayoutConstraint.Attribute.width,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: nil,
            attribute: NSLayoutConstraint.Attribute.notAnAttribute,
            multiplier: 1.0,
            constant: 30
        )
        let leadingConstraint = NSLayoutConstraint(
            item: self.weeklyButtons[i],
            attribute: NSLayoutConstraint.Attribute.leading,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: self.view,
            attribute: NSLayoutConstraint.Attribute.leading,
            multiplier: 1.0,
            constant: 100
        )

        let topConstraint = NSLayoutConstraint(
            item: self.weeklyButtons[i],
            attribute: NSLayoutConstraint.Attribute.top,
            relatedBy: NSLayoutConstraint.Relation.equal,
            toItem: self.view,
            attribute: NSLayoutConstraint.Attribute.top,
            multiplier: 1.0,
            constant: 100
        )
        self.weeklyButtons[i].addConstraint(heightConstraint)
        self.weeklyButtons[i].addConstraint(widthConstraint)
        self.weeklyButtons[i].addConstraint(leadingConstraint)
        self.weeklyButtons[i].addConstraint(topConstraint)
    

2019-03-07 14:38:59.176638-0500 Daily[27852:1408014] [LayoutConstraints] 视图层次结构没有为约束做好准备: 添加到视图时,约束的项必须是该视图(或视图本身)的后代。如果在组装视图层次结构之前需要解决约束,这将崩溃。中断 -[UIView(UIConstraintBasedLayout) _viewHierarchyUnpreparedForConstraint:] 进行调试。

2019-03-07 14:38:59.177816-0500 Daily[27852:1408014] [LayoutConstraints] 未准备好约束的视图层次结构。 约束: 容器层次结构: > | > 在容器层次结构中找不到视图:> 该视图的超级视图:NO SUPERVIEW

2019-03-07 14:38:59.192176-0500 Daily[27852:1408014] *** 由于未捕获的异常“NSGenericException”而终止应用程序,原因:“无法在视图上安装约束。约束是否引用了视图子树之外的内容?那是违法的。约束:视图:>'

【问题讨论】:

什么是selfself.view?这段代码在视图控制器中吗?视图控制器中的什么函数运行这段代码?似乎self.view 尚未添加到视图层次结构中。此外,您的按钮将彼此重叠,因为它们的前导约束均为 100。您可能需要考虑水平堆栈视图 【参考方案1】:
        self.weeklyButtons[i].addConstraint(leadingConstraint)

约束涉及weeklyButtons[i]self.view。如果使用addConstraint 激活约束,则必须将约束添加到两个视图的共同祖先。这就是此错误消息告诉您的内容:

添加到视图时,约束项必须是该视图(或视图本身)的后代。

由于self.viewweeklyButtons[i] 的父视图,因此它算作两个视图的共同祖先。所以在这种情况下,您可以将约束添加到self.view

self.view.addConstraint(leadingConstraint)

但不要那样做。ios 8 开始,你可以直接激活一个约束,UIKit 会自动将它添加到正确的视图中:

leadingConstraint.isActive = true

但不要这样做。由于您要连续添加四个约束,因此像这样同时激活四个约束可能会更有效:

NSLayoutConstraint.activate([
    leadingConstraint,
    topConstraint,
    widthConstraint,
    heightConstraint])

但不要那样做。从 iOS 9 开始,创建约束的方式变得更加易读:

NSLayoutConstraint.activate([
    self.weeklyButtons[i].heightAnchor.constraint(equalToConstant: 30),
    self.weeklyButtons[i].widthAnchor.constraint(equalToConstant: 30),
    self.weeklyButtons[i].leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 100),
    self.weeklyButtons[i].topAnchor.constraint(equalTo: view.topAnchor, constant: 100),
    ])

但不要那样做。使用for/in循环而不是索引变量i

func setupWeekdayButtons() 
    weeklyButtons = [mondayButton, tuesdayButton, wednesdayButton, thursdayButton, fridayButton, saturdayButton, sundayButton]

    for (button, title) in zip(weeklyButtons, weekdayLabels) 
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle(title, for: .normal)
        button.layer.borderColor = UIColor.black.cgColor
        button.titleLabel?.textAlignment = .center
        button.layer.borderWidth = 1.0
        button.layer.cornerRadius = 6.0
        button.setTitleColor(.black, for: .normal)
        button.setTitleColor(.red, for: .selected)
        button.addTarget(self, action: #selector(selectedDailyButton), for: .touchUpInside)
        view.addSubview(button)

        NSLayoutConstraint.activate([
            button.heightAnchor.constraint(equalToConstant: 30),
            button.widthAnchor.constraint(equalToConstant: 30),
            button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 100),
            button.topAnchor.constraint(equalTo: view.topAnchor, constant: 100),
            ])
    

这样做。

【讨论】:

非常感谢。直到今晚晚些时候我才能解决这个问题,但我相信这会有所帮助。很有帮助【参考方案2】:

自从我在代码中添加约束已经有一段时间了,但是如果没有记忆,您必须在添加约束之前将视图添加到视图层次结构中。 (这就是错误消息所暗示的内容。)

【讨论】:

你的记忆是正确的,但在这种情况下,这不是问题。

以上是关于以编程方式添加前导/顶部约束的主要内容,如果未能解决你的问题,请参考以下文章

NSLayout 约束失败

需要时以编程方式添加滚动

如何以编程方式添加标签并以编程方式使用自动布局定位它

UICollectionViewCell 以编程方式自动布局

iOS 如何设置约束以适应设备屏幕?

使用不使用硬编码的 CGRect 和最小数量的约束以编程方式将堆栈视图与屏幕顶部对齐