ios - Swift 约束以编程方式创建奇怪的行为

标签 ios xcode swift autolayout constraints

我正在尝试以编程方式布局所有约束,因为我必须根据用户输入添加和删除框。当我运行一个在 ViewDidLoad 中应用所有约束的函数时,它效果很好。但如果我再次运行它,将它们全部删除后,我的 2 个标签就会消失在导航栏后面。我不明白为什么!请记住,如果我从未运行下面粘贴的函数,我的屏幕将是空白的,因为我对从 Storyboard启动没有任何限制。我的目标是弄清楚为什么当第二次运行同一段代码时我的标签和按钮会消失。

下面是我将在下面发布的代码最初在 ViewDidLoad 中运行时的图片:

Constraints run during ViewDidLoad

下面是按下下一个按钮后再次运行同一段代码时的图片。这只是一个测试,将来我需要在更改后重置所有约束:

Constraints run again later (when next button is pressed)

下面是调试 View 层次结构的图片: enter image description here

创建约束的代码如下。请记住,我只有 5 个元素,我的主视图、 ScrollView 、作为标签的 aglAltitudeLabel、作为按钮的 nextButton 和作为文本输入的 aglAltitude:

    //Remove all pre-existing contraints
    var constraints:NSArray = aglAltitudeLabel.constraints()
    aglAltitudeLabel.removeConstraints(constraints)
    constraints = aglAltitude.constraints()
    aglAltitude.removeConstraints(constraints)
    constraints = nextButton.constraints()
    nextButton.removeConstraints(constraints)
    constraints = scrollView.constraints()
    scrollView.removeConstraints(constraints)
    constraints = view.constraints()
    self.view.removeConstraints(constraints)
    constraints = calculateButton.constraints()
    calculateButton.removeConstraints(constraints)

    //Make scrollview fit normal View, accounting for Navigation bar
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[first(\(self.view.frame.width))]|", options: nil, metrics: nil, views: ["first": scrollView]))
    self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-64-[first(\(self.view.frame.height-64))]|", options: nil, metrics: nil, views: ["first": scrollView]))

    //Set the heights for the label and button
    let labelHeight = NSLayoutConstraint(item: aglAltitudeLabel, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 20)
    scrollView.addConstraint(labelHeight)
    let buttonHeight = NSLayoutConstraint(item: nextButton, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1.0, constant: 30)
    scrollView.addConstraint(buttonHeight)

    //Setup the vertical layout
    scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-8-[first(30)]-8-|", options: nil, metrics: nil, views: ["first": aglAltitude]))


    //Setup the horizontal layout
    scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-8-[first(97)]-8-[second(>=100)]-8-[third(46)]-8-|", options: nil, metrics: nil, views: ["first": aglAltitudeLabel, "second": aglAltitude, "third": nextButton]))

    //Align baselines - this also occurs with the option above of AlignAllBaselines and leaving this code out
    let aglLabelBaseline = NSLayoutConstraint(item: aglAltitudeLabel, attribute: NSLayoutAttribute.Baseline, relatedBy: NSLayoutRelation.Equal, toItem: aglAltitude, attribute: NSLayoutAttribute.Baseline, multiplier: 1.0, constant: 0)
    scrollView.addConstraint(aglLabelBaseline)
    let nextButtonBaseline = NSLayoutConstraint(item: nextButton, attribute: NSLayoutAttribute.Baseline, relatedBy: NSLayoutRelation.Equal, toItem: aglAltitude, attribute: NSLayoutAttribute.Baseline, multiplier: 1.0, constant: 0)
    scrollView.addConstraint(nextButtonBaseline)

    self.view.layoutSubviews()

感谢您的帮助。我确信我做错了 55 件事。

编辑:如果我更改代码的最后一部分:

    //Align baselines - this also occurs with the option above of AlignAllBaselines and leaving this code out
    let aglLabelBaseline = NSLayoutConstraint(item: aglAltitudeLabel, attribute: NSLayoutAttribute.Baseline, relatedBy: NSLayoutRelation.Equal, toItem: aglAltitude, attribute: NSLayoutAttribute.Baseline, multiplier: 1.0, constant: 0)
    scrollView.addConstraint(aglLabelBaseline)
    let nextButtonBaseline = NSLayoutConstraint(item: nextButton, attribute: NSLayoutAttribute.Baseline, relatedBy: NSLayoutRelation.Equal, toItem: aglAltitude, attribute: NSLayoutAttribute.Baseline, multiplier: 1.0, constant: 0)
    scrollView.addConstraint(nextButtonBaseline)

对此:

    //Align baselines - this also occurs with the option above of AlignAllBaselines and leaving this code out
    let aglLabelBaseline = NSLayoutConstraint(item: aglAltitudeLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 14)
    self.view.addConstraint(aglLabelBaseline)
    let nextButtonBaseline = NSLayoutConstraint(item: nextButton, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: scrollView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 8)
    self.view.addConstraint(nextButtonBaseline)

它的工作原理完全符合我的预期。为什么不同?

最佳答案

在不知道您的要求是什么的情况下,很难给您好的建议。这是我如何做的一个例子。我在代码中添加了所有 View ,并为 View 添加了背景颜色,以便我可以更好地查看它们的布局。我假设 ScrollView 只添加一次,并且永远不会删除,所以它对 self.view 的约束应该只在 viewDidLoad 中添加一次。如果您不需要更改其他 View 的高度,它们也是如此(请注意,我将高度限制添加到 View 本身,而不是它的父 View )。唯一需要删除并重新添加的是 ScrollView 及其 subview 之间的约束。

class ViewController: UIViewController {

    var aglAltitudeLabel = UILabel()
    var aglAltitude = UITextField()
    var nextButton = UIButton.buttonWithType(.System) as UIButton
    var scrollView = UIScrollView()
    var viewsDict: NSDictionary!

    override func viewDidLoad() {
        super.viewDidLoad()
        aglAltitudeLabel.text = "AGL Altitude"
        aglAltitudeLabel.backgroundColor = UIColor.lightGrayColor()
        aglAltitudeLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        aglAltitude.setTranslatesAutoresizingMaskIntoConstraints(false)
        aglAltitude.backgroundColor = UIColor.yellowColor()
        aglAltitudeLabel.addConstraint(NSLayoutConstraint(item: aglAltitudeLabel, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30))
        scrollView.setTranslatesAutoresizingMaskIntoConstraints(false)
        nextButton.setTranslatesAutoresizingMaskIntoConstraints(false)
        nextButton.setTitle("Next", forState: .Normal)
        nextButton.backgroundColor = UIColor.lightGrayColor()
        nextButton.addConstraint(NSLayoutConstraint(item: nextButton, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 30))
        self.view.addSubview(scrollView)
        scrollView.addSubview(aglAltitude)
        scrollView.addSubview(aglAltitudeLabel)
        scrollView.addSubview(nextButton)
        scrollView.backgroundColor = UIColor(red: 1, green: 1, blue: 0.8, alpha: 1)

        viewsDict = ["scrollView": scrollView, "aglAltitudeLabel": aglAltitudeLabel, "aglAltitude":aglAltitude, "nextButton":nextButton] as NSDictionary

        self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|[scrollView]|", options: nil, metrics: nil, views: viewsDict))
        self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-64-[scrollView]|", options: nil, metrics: nil, views: viewsDict))

        self.redoConstraints()
    }


    @IBAction func redoConstraints() {

        scrollView.removeConstraints(scrollView.constraints())

        scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-8-[aglAltitude(30)]-8-|", options: nil, metrics: nil, views: viewsDict))

        scrollView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-8-[aglAltitudeLabel(97)]-8-[aglAltitude(>=100)]-8-[nextButton(46)]-8-|", options: .AlignAllBottom , metrics: nil, views: viewsDict))
    }
}

请注意,我将固定的宽度和高度排除在 ScrollView 的约束之外。没有必要这样做,这会导致它在轮换时无法正常工作。

关于ios - Swift 约束以编程方式创建奇怪的行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29049987/

相关文章:

xcode - 如何从 Applescript/Automator/Shell 脚本自动创建新的 Xcode 目标

ios - 试图让我的投票系统工作并在 Parse 中登录后端

ios - 如何在固定时间内为特定位置下方的指示箭头设置动画?

swift - 如何在 WKWebView 中使用带有钥匙串(keychain)访问的自动填充

ios - 即使创建了 Swift Bridging Header 文件,也无法在 Objective C 中引用 Swift 类

ios - NIMutableTableViewModel 错误 `AnyClass does not conform to NITableViewModelDelegate`

ios - 将 AnyObject 实现到 NSString 问题

cocoa - 如何将Cocoa静态库链接到C命令行目标?

ios - 尝试发布图片时获取 "FBSDKErrorDeveloperMessageKey=(#324) Requires upload file"

ios - UserDefaults UIButton 状态有时加载不正确