如何在 iOS 11 上修复 UILabel 内在内容大小

Sam*_*Sam 4 iphone uikit uilabel ios

在 iOS 11 上,由于标签明显误报了其内在内容大小,我们的许多布局都被破坏了。

当 UILabel 被包装在另一个试图实现intrinsicContentSize自身的视图中时,该错误似乎表现得最糟糕。像这样(简化和人为的例子):

class LabelView: UIView {

    let label = UILabel()

    override init(frame: CGRect) {

        super.init(frame: frame)

        self.setup()
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setup() {

        self.label.textColor = .black
        self.label.backgroundColor = .green
        self.backgroundColor = .red
        self.label.numberOfLines = 0
        self.addSubview(self.label)

        self.label.translatesAutoresizingMaskIntoConstraints = false
        self.label.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
        self.label.trailingAnchor.constraint(lessThanOrEqualTo: self.trailingAnchor).isActive = true
        self.label.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        self.label.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
    }

    override var intrinsicContentSize: CGSize {

        let size = self.label.intrinsicContentSize

        print(size)

        return size
    }
}
Run Code Online (Sandbox Code Playgroud)

intrinsicContentSize该的UILabel的是非常独特的,看起来是这样的:(width: 1073741824.0, height: 20.5)。这会导致布局周期为视图的包装器提供太多空间。

这仅在从 XCode 9 为 iOS 11 编译时发生。在 iOS 10 SDK(在 XCode 8 上)编译的 iOS 11 上运行时。

在 XCode 8 (iOS 10) 上,视图正确呈现,如下所示:

在此处输入图片说明

在 XCode 9 (iOS 11) 上,视图呈现如下:

XCode 9 渲染

演示此问题的带有完整操场代码的 Gist在这里

我已经为此提交了一份雷达,并且至少有一个解决问题的方法(请参阅下面的答案)。我想知道是否有其他人遇到过这个问题,或者有其他方法可以尝试。

Sam*_*Sam 5

因此,通过在操场上进行试验,我提出了一个解决方案,该解决方案涉及对极大的内在内容大小进行测试。

我注意到所有行为不端的 UILabels 都有numberOfLines==0preferredMaxLayoutWidth=0。在随后的布局过程中,UIKit 将 preferredMaxLayoutWidth 设置为非零值,大概是为了迭代到标签的正确高度。所以第一个解决方法是尝试临时设置numberOfLineswhen (self.label.numberOfLines == 0 && self.label.preferredMaxLayoutWidth == 0)

我还注意到所有具有这两个属性为 0 的 UILabels 不一定行为不端。(即反之不成立)。因此,此修复有效,但有时会不必要地修改标签。它还有一个小错误,当标签的文本包含\n换行符时,应将行数设置为字符串中的行数,而不是 1。

我找到的最终解决方案有点笨拙,但专门寻找 UILabel 行为不端,然后才踢它......

override var intrinsicContentSize: CGSize {

    guard super.intrinsicContentSize.width > 1000000000.0 else {

        return super.intrinsicContentSize
    }

    var count = 0

    if let text = self.text {

        text.enumerateLines {(_, _) in
            count += 1
        }

    } else {

        count = 1
    }

    let oldNumberOfLines = self.numberOfLines

    self.numberOfLines = count
    let size = super.intrinsicContentSize

    self.numberOfLines = oldNumberOfLines

    return size
}
Run Code Online (Sandbox Code Playgroud)

你可以在这里找到它作为 Gist 。

  • 我为这个问题找到了一个不那么笨拙的解决方案。修复是设置 UILabel 的 `preferredMaxLayoutWidth`。我将它设置为这样的屏幕宽度:`preferredMaxLayoutWidth = UIScreen.main.bounds.width` (3认同)