没有内在尺寸的自动布局

Posted

技术标签:

【中文标题】没有内在尺寸的自动布局【英文标题】:Autolayout without intrinsic size 【发布时间】:2021-11-20 07:47:18 【问题描述】:

谁能告诉我 Autolayout 如何知道 UIView 的大小,它没有固有大小并且只有一个维度受到限制?

例如,我有一个包含一组子视图的普通视图。视图被限制在父级的前后页边距并垂直居中。 Autolayout 能够向视图询问给定宽度的高度,但我不知道如何。我需要手动布局子视图,但看不到需要实现的功能/属性,因此 Autolayout 可以告诉我宽度并询问相应的高度。

有人知道吗?

【问题讨论】:

“我需要手动布置子视图”——你是说在 Storyboard 中吗?或者你的意思是通过代码?一般来说,你要么给视图一个高度,并让约束布局它的子视图,或者......你让子视图上的约束决定视图的高度。 在代码中。我不能在这个视图中使用自动布局,因为布局很复杂。我知道如果可以的话,一切都会奏效。 【参考方案1】:

作为一般规则,布局越复杂,更多使用自动布局的理由。

但是,如果您真的想“手动布局”您的子视图并计算内在大小,您可以通过覆盖 intrinsicContentSize 来实现。

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

这是一个简单的例子。我们添加两个子视图,一个在另一个之上。我们给顶视图一个 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),
        ])
        
    
    

我们得到这个输出:

【讨论】:

感谢 DonMag 的全面建议,这是一个有趣的想法。它归结为返回“noIntrinsicSize”的初始固有宽度和零高度,并且在 layoutSubviews 的第一遍使用 bounds.width 来计算高度。我会试试看。 @jspinks - 如果您想查看更“实用”的示例,请查看此答案的 Edit 部分中的 TagLabelsView 类:@987654323 @

以上是关于没有内在尺寸的自动布局的主要内容,如果未能解决你的问题,请参考以下文章

没有自动布局的不同屏幕尺寸的相同故事板

自动布局内在内容大小以填满屏幕

由于自动布局没有计算尺寸,将子视图添加到自定义视图最终会出错

为自动布局设置数字 UILabel 以正确计算内在内容大小

自动布局视图在不同的屏幕尺寸上总是相同的尺寸

不同设备尺寸的 iOS 自动布局约束值