在代码中设置自动布局约束时出现奇怪的问题

Posted

技术标签:

【中文标题】在代码中设置自动布局约束时出现奇怪的问题【英文标题】:Weird issue when setting up auto-layout constraints in code 【发布时间】:2014-11-08 13:01:59 【问题描述】:

我想在具有三个等距标签的静态单元 UITableView 上有一个页脚视图,就像这样(来自模拟器):

我可以使用这个委托调用从我的表格视图控制器提供页脚视图:

override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? 
    // construct view here
    return view

我可以通过两种方式构建视图:

在代码中创建标签和分隔符并添加适当的约束 在 XIB 文件中执行所有操作,然后从文件中加载视图

我的问题是第一种方法不起作用,而第二种方法起作用。

这是我的第一种方法的代码:

override func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? 

    if section == 0 

        // Create footer view
        let view = UIView()
        view.backgroundColor = UIColor.yellowColor()
        view.clipsToBounds = false
        view.layer.borderColor = UIColor.greenColor().CGColor
        view.layer.borderWidth = 2
        view.setTranslatesAutoresizingMaskIntoConstraints(false)

        // Create labels
        var labels: [UIView] = []
        for name in ["Label 1", "AAAAAABBB", "Last label"] 
            let v = UILabel()

            v.font = UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote)
            v.textColor = UIColor.darkTextColor()
            v.textAlignment = .Center
            v.text = name
            v.setTranslatesAutoresizingMaskIntoConstraints(false)

            view.addSubview(v)
            labels += [v]
        

        // Create spacers
        var spacers: [UIView] = []
        for i in 1...4 
            let v = UIView()

            v.backgroundColor = UIColor.blueColor() // Background color is just so we can see where the view is and what size it has
            v.setTranslatesAutoresizingMaskIntoConstraints(false)

            view.addSubview(v)
            spacers += [v]
        

        // Constrain all views to top and bottom of superview
        for i in labels + spacers 
            view.addConstraint(NSLayoutConstraint(item: i, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0))
            view.addConstraint(NSLayoutConstraint(item: i, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1, constant: 0))
        

        // Equal width for labels
        labels.pairs 
            view.addConstraint(NSLayoutConstraint(item: $0, attribute: .Width, relatedBy: .Equal, toItem: $1, attribute: .Width, multiplier: 1, constant: 0))
        

        // Equal width for spacers
        spacers.pairs 
            view.addConstraint(NSLayoutConstraint(item: $0, attribute: .Width, relatedBy: .Equal, toItem: $1, attribute: .Width, multiplier: 1, constant: 0))
        

        view.addConstraint(NSLayoutConstraint(item: view, attribute: .Left, relatedBy: .Equal, toItem: spacers[0], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: spacers[0], attribute: .Right, relatedBy: .Equal, toItem: labels[0], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: labels[0], attribute: .Right, relatedBy: .Equal, toItem: spacers[1], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: spacers[1], attribute: .Right, relatedBy: .Equal, toItem: labels[1], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: labels[1], attribute: .Right, relatedBy: .Equal, toItem: spacers[2], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: spacers[2], attribute: .Right, relatedBy: .Equal, toItem: labels[2], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: labels[2], attribute: .Right, relatedBy: .Equal, toItem: spacers[3], attribute: .Left, multiplier: 1, constant: 0))
        view.addConstraint(NSLayoutConstraint(item: spacers[3], attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1, constant: 0))

        return view
    
    else 
        return nil
    


extension Array 
    func pairs(block: (Element, Element?)->()) 
        if count == 0  return 
        if count == 1  block(self.first!, nil) 

        var last = self[0]
        for i in self[1..<count] 
            block(last, i)
            last = i
        
    

这是结果:

完全不是我所期待的,对吧?

现在,进入第二种方法。我没有从 Interface Builder 发布一堆屏幕截图,而是创建了一个可用的示例项目 here 专门用于测试这个问题。如果打开它,文件FooterView.xib 包含在 IB 中构建的页脚视图,据我所知,它具有完全相同的视图结构和自动布局约束。

使用该视图,如下所示:

return (NSBundle.mainBundle().loadNibNamed("FooterView", owner: self, options: nil).first as UIView)

产生您在第一个屏幕截图中看到的结果,这正是我想要的。

因此,使用 Interface Builder,约束按预期工作。 为什么在代码中创建视图和约束时它不起作用?我错过了什么?

【问题讨论】:

【参考方案1】:

视图的大小在创建时是未知的,因此将其 setTranslatesAutoresizingMaskIntoConstraints设置为 true 就可以了:

view.setTranslatesAutoresizingMaskIntoConstraints(true)

结果:

【讨论】:

有效!但为什么? setTranslatesAutoresizingMaskIntoConstraints 与创建时未知的大小有什么关系? 我注意到视图没有得到tableView.frame.width 的宽度并尝试手动设置。不幸的是,这不起作用,所以我玩了setTranslatesAutoresizingMaskIntoConstraints。我认为视图现在会自动将其大小调整为 tableView 宽度。 很高兴为您提供帮助!顺便说一句:使用 Array 扩展设置约束的方法真的很酷。

以上是关于在代码中设置自动布局约束时出现奇怪的问题的主要内容,如果未能解决你的问题,请参考以下文章

在 Java 对象的 Kotlin 中设置属性时出现奇怪的“无法重新分配 Val”错误

尝试在 lambda 函数中设置按钮标签时出现分段错误

代码中设置的自动布局约束未出现在界面生成器中

使用选项卡控制器时出现奇怪的滚动行为 - iOS

在 .htaccess 中设置 php_value 时出现意外错误?

在 ListView 中设置页脚布局时出现问题