如果你想让一个视图强制在其父视图上进行宽度填充,它实际上就像设置 .greatestFiniteMagnitude 一样简单吗?

问题描述 投票:0回答:1

例如,假设您有一个垂直堆栈视图。它的对齐方式是领先的(因为您希望大多数项目向左收缩)。

但是,您有一种特殊类型的项目,您想要全宽。

我总是采用某种复杂的解决方案来实现这一目标:

class FullWidthThing: .. {
    
    ..
        translatesAutoresizingMaskIntoConstraints = false
        
    private lazy var fullWidth: NSLayoutConstraint = {
        guard let sv = superview else { return NSLayoutConstraint() }
        let v = widthAnchor.constraint(equalTo: sv.widthAnchor)
        v.isActive = true
        return v
    }()
    
    override func layoutSubviews() {
        if superview != nil {
            _ = fullWidth
        }
        super.layoutSubviews()
    }
}

但这实际上就像将内在函数设置为

.greatestFiniteMagnitude
一样简单吗?表面测试表明这似乎有效:

private lazy var setup: () = {
    translatesAutoresizingMaskIntoConstraints = false
    setContentCompressionResistancePriority(.required, for: .horizontal)
    ...
    return ()
}()

override var intrinsicContentSize: CGSize {
    var sz = super.intrinsicContentSize
    sz.width = .greatestFiniteMagnitude
    return sz
}

有人明确知道这是否有效,或者这样做有问题,或者这样做的常见方法吗?

ios swift uikit intrinsic-content-size
1个回答
0
投票

对于任何视图,自动布局(无论是在堆栈视图还是其他层次结构中)都将使用 .intrinsicContentSize,除非其他因素影响大小。

考虑这个布局:

enter image description here

黄色框架是垂直堆栈视图,具有顶部和前导约束,间距:12 和对齐方式:前导

每个轮廓框架也是一个垂直堆栈视图,分布:均匀填充,间距:4,对齐方式:前导,包含一个标签和一个(默认)

UIView

我们看不到视图,因为它们没有宽度。

每个“子”堆栈视图的宽度与其标签的固有宽度相匹配,并且外部堆栈视图具有最宽的“子”堆栈视图的宽度。

Outer Stack width: 300.0 (intrinsic width of widest label)

现在,让我们添加另一个子堆栈视图,其标签太宽而无法适应屏幕:

enter image description here

Outer Stack width: 833.6666666666666

接下来,让我们向外部堆栈视图添加一个 Trailing 约束:

enter image description here

Outer Stack width: 353.0

并且,第四根弦较长:

enter image description here

Outer Stack width: 353.0 (still)

那么,如果我们使用子类“宽”视图而不是默认的

UIView
,会发生什么:

class WideView: UIView {
    override var intrinsicContentSize: CGSize {
        var sz = super.intrinsicContentSize
        sz.width = .greatestFiniteMagnitude
        return sz
    }
}

.greatesFiniteMagnitude
的值是
1.7976931348623157e+308
...如果我们将其格式化为浮点数(“%0.0f”),我们会得到一个长度为309位的数字。

这是我们通过前 3 个字符串得到的结果,外部堆栈视图上没有尾随锚点:

enter image description here

Outer Stack width: 2777777.0 (greatest width allowed by UIKit)

如果我们添加更长的字符串:

enter image description here

看起来一样,再说一遍:

Outer Stack width: 2777777.0 

添加外部堆栈视图尾随锚点:

enter image description here

Outer Stack width: 353.0

这是我用来生成这些输出的类(请参阅注释和注释行):

class WideTestVC: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .systemYellow
        
        let strs: [String] = [
            "Short",
            "Medium length",
            "Longer string for width testing",
            //"This string will be too long to fit the available width of a phone in portrait orientation!",
        ]
        let colors: [UIColor] = [
            .systemRed,
            .systemGreen,
            .systemBlue,
            .cyan,
        ]
        
        let outerStack = UIStackView()
        outerStack.axis = .vertical
        outerStack.alignment = .leading
        outerStack.spacing = 12
        
        for (str, c) in zip(strs, colors) {
            let st = UIStackView()
            st.axis = .vertical
            st.alignment = .leading
            st.distribution = .fillEqually
            st.spacing = 4
            st.layer.borderColor = UIColor.darkGray.cgColor
            st.layer.borderWidth = 1
            
            let label = UILabel()
            label.font = .systemFont(ofSize: 24.0, weight: .light)
            label.text = str
            label.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
            
            // to see the difference...
            // use standard UIView (will have no width)
            let v = UIView()
            // or, use custom "WideView" with width: .greatestFiniteMagnitude
            //let v = WideView()
            
            v.backgroundColor = c
            st.addArrangedSubview(label)
            st.addArrangedSubview(v)
            outerStack.addArrangedSubview(st)
        }
        
        outerStack.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(outerStack)
        
        let g = view.safeAreaLayoutGuide
        
        // constrain ONLY Top / Leading, or
        // constrain Top / Leading / Trailing
        NSLayoutConstraint.activate([
            outerStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 30.0),
            outerStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            //outerStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
        ])
        
        outerStack.backgroundColor = .yellow
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let v = view.subviews.first as? UIStackView
        else { return }
        
        print(v.frame.width)
        
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.