没有固有尺寸的自动布局

Autolayout without intrinsic size

谁能告诉我 Autolayout 是如何知道 UIView 的大小的,它没有固有大小,只有一个维度受到约束?

例如,我有一个包含一组子视图的普通视图。视图受限于父级的前导和尾随边距,并垂直居中。自动布局能够向视图询问给定宽度的高度,但我不知道如何。我需要手动布局子视图,但我看不到我需要实现哪个 function/property,因此 Autolayout 可以告诉我宽度并要求相应的高度。

有人知道吗?

一般来说,布局越复杂,更多使用自动布局的理由。

但是,如果您真的想“手动布局”您的子视图并计算固有大小,您可以通过重写 intrinsicContentSize.

来实现

因此,例如,如果您计算您在layoutSubviews()中的子视图大小/位置,您还应该计算那里的高度,调用invalidateIntrinsicContentSize(),和 return 您计算出的身高。

这是一个简单的例子。我们添加两个子视图,一个在另一个之上。我们给顶视图一个 4:1 比例,给底视图一个 3:1 比例。

class MyIntrinsicView: UIView {
    
    var myHeight: CGFloat = 0
    
    let view1: UIView = {
        let v = UIView()
        v.backgroundColor = .red
        return v
    }()
    let view2: UIView = {
        let v = UIView()
        v.backgroundColor = .green
        return v
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    
    func commonInit() -> Void {
        addSubview(view1)
        addSubview(view2)
        backgroundColor = .blue
    }
    
    override var intrinsicContentSize: CGSize {
        var sz = super.intrinsicContentSize
        sz.height = myHeight
        return sz
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        // let's say view 1 needs a 4:1 ratio and
        //  view 2 needs a 3:1 ratio
        view1.frame = CGRect(x: 0, y: 0, width: bounds.width, height: bounds.width / 4.0)
        view2.frame = CGRect(x: 0, y: view1.frame.maxY, width: bounds.width, height: bounds.width / 3.0)

        myHeight = view1.frame.height + view2.frame.height
        invalidateIntrinsicContentSize()
        
    }
    
}

使用这个示例视图控制器,我们给视图 60 磅的前导和尾随,并且只有 centerY 约束:

class QuickExampleViewController: UIViewController {
    
    let testView = MyIntrinsicView()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        testView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(testView)
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            testView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 60.0),
            testView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -60.0),
            testView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
        ])
        
    }
    
}

我们得到这个输出: