使用swift在代码中的约束 - 无法激活

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用swift在代码中的约束 - 无法激活相关的知识,希望对你有一定的参考价值。

我正在尝试了解自动布局限制。遵循Ray W教程挑战(没有解决方案或讨论),布局应如下所示:

desired layout

在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>>的约束,因为它们没有共同的祖先。约束是否引用不同视图层次结构中的项目?这是非法的。

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

经过很多痛苦,我试图完全重新创建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.
    }


}
答案

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

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


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

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

self.centerView.addSubview(passwordLabel)

这就是给你错误的原因。 passwordLabel不在视图层次结构中,因此Auto Layout不知道如何用centerView约束它。

你的第二个问题是你将widthheightcenterView设置为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)
另一答案

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

我得到了错误,因为我在将视图添加到其superview之前引用了我的superview。 < - 编写代码本身与不包括view.addSubview(stackView)本身没什么不同。两者都会生成相同的错误 - 因为视图与其superview没有定义的关系。

在下面的代码中,在我将视图添加到superview之前,行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

    }
}

以上是关于使用swift在代码中的约束 - 无法激活的主要内容,如果未能解决你的问题,请参考以下文章

无法激活约束

使用swift iOS8通过代码将约束设置到Storyboard中的元素

使用 Swift 动画约束更改(第二项)?

Swift - Xcode 8 - iOS 10 - 无法创建简单约束

如何在Swift 4中的watchOS 4上激活振动

为 iOS [Swift、iOS 15] 编写 PDF 查看器时无法激活三次轻击手势