【问题标题】:How to make a custom view's frame match its instrinsic content size如何使自定义视图的框架与其内在内容大小相匹配
【发布时间】:2016-04-02 08:43:12
【问题描述】:

根据内容调整大小的自定义标签

我正在从头开始制作标签。 (我的最终目的是为蒙古文字制作一个垂直文本标签,但现在我只是作为练习制作一个普通的水平文本标签。)

只要没有长度和宽度限制,我希望我的自定义标签的框架根据其内在内容大小调整大小。

它适用于 IB

在 IB 中,当我测试 UILabel、我的自定义标签(UIView 子类)和按钮时,一切似乎都运行良好。

我为每个视图添加顶部和前导约束,但没有设置任何高度或宽度约束。

如果我在情节提要上调整它们的大小(但仍然没有添加任何约束)...

然后为 View Controller 中的所有视图 选择 Update Frames,普通标签和我的自定义标签都会正确调整到它们固有的内容大小。

它不适用于正在运行的应用程序

但是,当我在运行时更改标签文本时,我的自定义标签的框架没有调整大小。 (我暂时在文本层添加了一个深蓝色边框,以帮助这里与自定义标签的框架区分开来,即浅蓝色背景色。)

单击“更改文本”会给出

如您所见,文本层框架发生了变化,但自定义视图的框架没有发生变化。

代码

这是我的自定义标签类:

import UIKit
@IBDesignable
class UILabelFromScratch: UIView {

    private let textLayer = CATextLayer()

    @IBInspectable var text: String = "" {
        didSet {
            updateTextLayerFrame()
        }
    }

    @IBInspectable var fontSize: CGFloat = 17 {
        didSet {
            updateTextLayerFrame()
        }
    }

    // MARK: - Initialization

    override init(frame: CGRect) {
        super.init(frame: frame)
        setup()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }

    func setup() {

        // Text layer
        textLayer.borderColor = UIColor.blueColor().CGColor
        textLayer.borderWidth = 1
        textLayer.contentsScale = UIScreen.mainScreen().scale
        layer.addSublayer(textLayer)
    }

    // MARK: Other methods

    override func intrinsicContentSize() -> CGSize {
        return textLayer.frame.size
    }

    func updateTextLayerFrame() {

        let myAttribute = [ NSFontAttributeName: UIFont.systemFontOfSize(fontSize) ]
        let attrString = NSMutableAttributedString(string: self.text, attributes: myAttribute )
        let size = dimensionsForAttributedString(attrString)

        textLayer.frame = CGRect(x: self.layer.bounds.origin.x, y: self.layer.bounds.origin.y, width: size.width, height: size.height)
        textLayer.string = attrString

    }

    func dimensionsForAttributedString(attrString: NSAttributedString) -> CGSize {

        var ascent: CGFloat = 0
        var descent: CGFloat = 0
        var width: CGFloat = 0
        let line: CTLineRef = CTLineCreateWithAttributedString(attrString)
        width = CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, nil))

        // make width an even integer for better graphics rendering
        width = ceil(width)
        if Int(width)%2 == 1 {
            width += 1.0
        }

        return CGSize(width: width, height: ceil(ascent+descent))
    }
}

这里是视图控制器类:

import UIKit
class ViewController: UIViewController {

    @IBOutlet weak var normalLabel: UILabel!
    @IBOutlet weak var labelFromScratch: UILabelFromScratch!

    @IBAction func changeTextButtonTapped(sender: UIButton) {

        normalLabel.text = "Hello"
        labelFromScratch.text = "Hello"
    }
}

问题

我的自定义标签中缺少UILabel 是什么?我覆盖了intrinsicContentSize

override func intrinsicContentSize() -> CGSize {
    return textLayer.frame.size
}

我还需要做什么?

【问题讨论】:

    标签: ios swift autolayout uilabel intrinsic-content-size


    【解决方案1】:

    我遗漏了一行代码:

    invalidateIntrinsicContentSize()
    

    我是在更新文本图层框架后添加的。

    func updateTextLayerFrame() {
    
        // ...
    
        textLayer.frame = CGRect(x: self.layer.bounds.origin.x, y: self.layer.bounds.origin.y, width: size.width, height: size.height)
        invalidateIntrinsicContentSize()
    
        // ...
    }
    

    documentation

    当您的自定义视图中发生无效的更改时调用此函数 它的内在内容大小。这允许基于约束的布局 系统将新的内在内容大小考虑在内 布局通行证。

    我在哪里找到了这个解决方案:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-16
      • 2019-02-06
      • 1970-01-01
      • 2012-06-05
      相关资源
      最近更新 更多