【问题标题】:Constraints in code using swift - unable to activate使用 swift 的代码中的约束 - 无法激活
【发布时间】:2015-12-03 10:12:09
【问题描述】:

我正在尝试理解自动布局约束。在 Ray W 教程挑战之后(没有解决方案或讨论),布局应如下所示:

在 IB 中执行此操作非常简单 - 创建一个具有宽度和高度的黄色视图,垂直居中并水平固定到边距;然后在该视图中创建带有简单约束的标签、字段和按钮。

我的第一个问题是:如果标签具有固有的内容大小,并且其他所有内容都固定在黄色视图上,那么为什么我需要定义固定的宽度和高度?为什么它不从其子视图的内在内容推断宽度和高度?

继续并尝试在代码中重新创建此布局给了我一个错误:

由于未捕获的异常“NSGenericException”而终止应用程序, 原因:'无法使用项目 <UILabel: 0x7a961d60; frame = (0 0; 0 0); text = 'Password:'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x7a961e40>> 和激活约束 <UIView: 0x7a96b6f0; frame = (0 0; 0 0); layer = <CALayer: 0x7a96b880>> 因为他们没有共同的祖先。是否 不同视图层次结构中的约束引用项?那是 非法。'

第二个问题:这些层是什么,我所有的视图都在一个层次结构中 - 超级视图包含黄色视图包含文本标签和字段。

痛苦万分后,我尝试完全重新创建 IB 中的约束,但这只会添加以下错误: 无法同时满足约束。

(
    "<NSLayoutConstraint:0x7ac57370 H:[UIView:0x7a96b6f0(0)]>",
    "<NSLayoutConstraint:0x7ac57400 H:|-(8)-[UILabel:0x7a96bb50'Username:']   (Names: '|':UIView:0x7a96b6f0 )>",
    "<NSLayoutConstraint:0x7ac57430 UILabel:0x7a96bb50'Username:'.trailing == UITextField:0x7a961020.leading + 8>",
    "<NSLayoutConstraint:0x7ac57520 UITextField:0x7a961020.trailing == UIView:0x7a96b6f0.trailing - 8>" )

最后一个问题:我如何知道视图 0x7aetc 是什么视图?我的代码中的这个约束在哪里?其余的看起来还可以(?)。

我一定是在某些基本层面上做错了什么。

这是我的代码:

import UIKit

class ViewController: UIViewController {

    let centerView = UIView()
    let usernameLabel = UILabel()
    let passwordLabel = UILabel()
    let usernameField = UITextField()
    let passwordField = UITextField()
    let submitButton = UIButton()

    override func viewDidLoad() {
        super.viewDidLoad()

        centerView.backgroundColor = UIColor.yellowColor()
        usernameLabel.text = "Username:"
        passwordLabel.text = "Password:"
        usernameField.borderStyle = .RoundedRect
        passwordField.borderStyle = .RoundedRect
        submitButton.setTitle("Submit!", forState: .Normal)
        submitButton.setTitleColor(UIColor.blackColor(), forState: .Normal)
        submitButton.setTitleColor(UIColor.blueColor(), forState: .Highlighted)

        view.addSubview(centerView)
        self.centerView.addSubview(usernameField)
        self.centerView.addSubview(passwordField)
        self.centerView.addSubview(usernameLabel)
        self.centerView.addSubview(submitButton)

        let constraintCenterViewHeight = NSLayoutConstraint(item: centerView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 0)
        let constraintCenterViewWidth = NSLayoutConstraint(item: centerView, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0, constant: 0)

        let constraintCenterViewCenterX = NSLayoutConstraint(item: centerView, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1.0, constant: 0)
        let constraintCenterViewCenterY = NSLayoutConstraint(item: centerView, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1.0, constant: 0)

        let constraintUsernameLabelHLeading = NSLayoutConstraint(item: usernameLabel, attribute: .Leading, relatedBy: .Equal, toItem: centerView, attribute: .Leading, multiplier: 1.0, constant: 8)
        let constraintUsernameLabelHTrailing = NSLayoutConstraint(item: usernameLabel, attribute: .Trailing, relatedBy: .Equal, toItem: usernameField, attribute: .Leading, multiplier: 1.0, constant: 8)
        let constraintUsernameLabelAlignBottom = NSLayoutConstraint(item: usernameLabel, attribute: .Bottom, relatedBy: .Equal, toItem: usernameField, attribute: .Bottom, multiplier: 1.0, constant: 0)

        let constraintUsernameFieldVTop = NSLayoutConstraint(item: usernameField, attribute: .Top, relatedBy: .Equal, toItem: centerView, attribute: .Top, multiplier: 1.0, constant: 8)
        let constraintUsernameFieldHTrailing = NSLayoutConstraint(item: usernameField, attribute: .Trailing, relatedBy: .Equal, toItem: centerView, attribute: .Trailing, multiplier: 1.0, constant: -8)
        let constraintUsernameFieldVBottom = NSLayoutConstraint(item: usernameField, attribute: .Bottom, relatedBy: .Equal, toItem: passwordField, attribute: .Top, multiplier: 1.0, constant: 8)

        let constraintPasswordLabelHLeading = NSLayoutConstraint(item: passwordLabel, attribute: .Leading, relatedBy: .Equal, toItem: centerView, attribute: .Leading, multiplier: 1.0, constant: 8)
        let constraintPasswordLabelHTrailing = NSLayoutConstraint(item: passwordLabel, attribute: .Trailing, relatedBy: .Equal, toItem: passwordField, attribute: .Leading, multiplier: 1.0, constant: 8)
        let constraintPasswordLabelAlignBottom = NSLayoutConstraint(item: passwordLabel, attribute: .Bottom, relatedBy: .Equal, toItem: passwordField, attribute: .Bottom, multiplier: 1.0, constant: 0)

        let constraintPasswordFieldHTrailing = NSLayoutConstraint(item: passwordField, attribute: .Trailing, relatedBy: .Equal, toItem: centerView, attribute: .Trailing, multiplier: 1.0, constant: -8)

        centerView.setTranslatesAutoresizingMaskIntoConstraints(false)
        usernameLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        usernameField.setTranslatesAutoresizingMaskIntoConstraints(false)
        passwordField.setTranslatesAutoresizingMaskIntoConstraints(false)
        passwordLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
       // submitButton.setTranslatesAutoresizingMaskIntoConstraints(false)

        NSLayoutConstraint.activateConstraints([constraintCenterViewHeight, constraintCenterViewWidth, constraintCenterViewCenterX, constraintCenterViewCenterY, constraintUsernameLabelHLeading,
            constraintUsernameLabelHTrailing, constraintUsernameLabelAlignBottom, constraintUsernameFieldVTop, constraintUsernameFieldHTrailing, constraintUsernameFieldVBottom, constraintPasswordLabelHLeading, constraintPasswordLabelHTrailing, constraintPasswordLabelAlignBottom, constraintPasswordFieldHTrailing])
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

【问题讨论】:

    标签: swift autolayout nslayoutconstraint


    【解决方案1】:

    我的第一个问题是:如果标签具有固有的内容大小, 其他所有内容都固定在黄色视图上,那我为什么需要 定义一个固定的宽度和高度?为什么它不能推断宽度 和子视图的内在内容的高度?

    usernamepassword 字段没有宽度。它们通过固定在centerView 左右边缘的标签上来获得宽度。如果您添加了约束来为这些字段指定宽度,那么centerView 可以根据其内容确定其宽度。


    继续并尝试在代码中重新创建此布局给了我一个 错误:由于未捕获的异常“NSGenericException”而终止应用程序, 原因:'无法使用项目 > 和 > 激活约束,因为它们 没有共同的祖先。约束是否引用中的项目 不同的视图层次结构?这是非法的。'

    您的第一个问题是您没有将passwordLabel 添加为子视图:

    self.centerView.addSubview(passwordLabel)
    

    这就是给你错误的原因。 passwordLabel 不在视图层次结构中,因此自动布局不知道如何使用 centerView 对其进行约束。

    您的第二个问题是您将centerViewwidthheight 设置为0。尝试更大的值,例如 100300

    let constraintCenterViewHeight = NSLayoutConstraint(item: centerView, attribute: .Height,
        relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0,
        constant: 100)
    
    let constraintCenterViewWidth = NSLayoutConstraint(item: centerView, attribute: .Width,
        relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1.0,
        constant: 300)
    

    您的第三个问题是您的某些常量需要为负数:

    // changed to -8
    let constraintUsernameLabelHTrailing = NSLayoutConstraint(item: usernameLabel,
        attribute: .Trailing, relatedBy: .Equal, toItem: usernameField,
        attribute: .Leading, multiplier: 1.0, constant: -8)
    
    // changed to -8
    let constraintUsernameFieldVBottom = NSLayoutConstraint(item: usernameField,
        attribute: .Bottom, relatedBy: .Equal, toItem: passwordField,
        attribute: .Top, multiplier: 1.0, constant: -8)
    
    // changed to -8
    let constraintPasswordLabelHTrailing = NSLayoutConstraint(item: passwordLabel,
        attribute: .Trailing, relatedBy: .Equal, toItem: passwordField,
        attribute: .Leading, multiplier: 1.0, constant: -8)
    

    【讨论】:

    • 我看了这段代码好几天了,从来没有看到丢失的子视图,也没有看到宽度和高度为0!啊哈,(正如我写的那样,我没有得到它)​​我迟钝的老大脑认为 usernameLabel 的后沿到 usernameField 的前沿应该沿 Y 或水平轴进一步 8 个点。但这只是另一个愚蠢的错误。我希望 usernameLabel 的后沿到 usernameField 的前沿沿 Y 或水平轴向后 8 个点。非常抱歉,非常感谢您指出这一点。很高兴知道如果您理解了这些东西,它会起作用。
    【解决方案2】:

    我遇到了同样的错误,但发生的原因不同:

    我收到错误是因为我引用了我的 superview 我将视图添加到它的 superview 之前。 view.addSubview(stackView) 本身没有什么不同。两者都会产生相同的错误——因为视图与其父视图没有定义的关系。

    在下面的 my 代码中,B1、B2 行发生在我将视图添加到超级视图之前(在 AA 行)。

    这是我的代码:

    class ViewController: UIViewController {
    
        var label = UILabel()
        var button = UIButton()
        var stackView = UIStackView()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = UIColor.cyan
            navigationItem.title = "VCTitle"
            setupUI()
        }
    
        private func setupUI(){
    
            label.backgroundColor = UIColor.blue
            label.heightAnchor.constraint(equalToConstant: 50).isActive = true
            label.widthAnchor.constraint(equalToConstant: 80).isActive = true
    
            button.backgroundColor = UIColor.purple
            button.heightAnchor.constraint(equalToConstant: 30).isActive = true
            button.widthAnchor.constraint(equalToConstant: 10).isActive = true
    
            setupStackView()
    
            stackView.addArrangedSubview(label)
            stackView.addArrangedSubview(button)
            stackView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(stackView) // AA
    
    
        }
    
        private func setupStackView(){
    
            stackView.axis = UILayoutConstraintAxis.vertical
            stackView.distribution = UIStackViewDistribution.equalSpacing
            stackView.alignment = UIStackViewAlignment.center
            stackView.spacing = 15
    
            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true // B1
            stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true // B2
    
        }
    
    
    }
    

    正确的做法是将 B1、B2 行移动到 AA 行之后的任意位置。

    class ViewController: UIViewController {
    
        var label = UILabel()
        var button = UIButton()
        var stackView = UIStackView()
    
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = UIColor.cyan
            navigationItem.title = "VCTitle"
            setupUI()
        }
    
        private func setupUI(){
    
            label.backgroundColor = UIColor.blue
            label.heightAnchor.constraint(equalToConstant: 50).isActive = true
            label.widthAnchor.constraint(equalToConstant: 80).isActive = true
    
            button.backgroundColor = UIColor.purple
            button.heightAnchor.constraint(equalToConstant: 30).isActive = true
            button.widthAnchor.constraint(equalToConstant: 10).isActive = true
    
            setupStackView()
    
            stackView.addArrangedSubview(label)
            stackView.addArrangedSubview(button)
            stackView.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(stackView) //AA
    
            // The 2 lines below are now in the right place.
            stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true // B1
            stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true // B2
    
    
        }
    
        private func setupStackView(){
    
            stackView.axis = UILayoutConstraintAxis.vertical
            stackView.distribution = UIStackViewDistribution.equalSpacing
            stackView.alignment = UIStackViewAlignment.center
            stackView.spacing = 15
    
        }
    }
    

    【讨论】:

    • 2 行是目标(centerXAnchor 和 centerYAnchor)应该报告给所有的 stackView 布局指南。你是今天的英雄,谢谢。
    猜你喜欢
    • 1970-01-01
    • 2018-04-20
    • 1970-01-01
    • 1970-01-01
    • 2020-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多