Как сделать так, чтобы рамка пользовательского представления соответствовала своему внутреннему размеру контента

Пользовательская метка, которая изменяет размеры в соответствии с ее содержанием

Я делаю этикетку с нуля. (Моя конечная цель - сделать вертикальную текстовую метку для монгольского шрифта, но сейчас я просто делаю обычную горизонтальную текстовую метку на практике.)

Пока нет ограничений по длине и ширине, я хочу, чтобы размер рамки моей пользовательской метки изменялся в соответствии с ее внутренним размером содержимого.

Работает в IB

В IB все работает нормально, когда я тестирую UILabelмой пользовательский ярлык (а UIView подкласс) и кнопка.

Я добавляю верхние и ведущие ограничения к каждому из этих видов, но не устанавливаю никаких ограничений по высоте или ширине.

введите описание изображения здесь

Если я изменю их размер на раскадровке (но все же без добавления каких-либо ограничений)...

введите описание изображения здесь

А затем выберите " Обновить кадры для всех представлений в View Controller", обычная метка и моя настраиваемая метка будут соответственно изменены в соответствии с их внутренними размерами содержимого.

введите описание изображения здесь

Это не работает на работающем приложении

Когда я изменяю текст метки во время выполнения, тем не менее, размер рамки моей нестандартной метки не изменяется. (Я временно добавил темно-синюю рамку к текстовому слою, чтобы отличить ее здесь от рамки пользовательской метки, которая представляет собой светло-синий цвет фона.)

введите описание изображения здесь

Нажатие "Изменить текст" дает

введите описание изображения здесь

Как видите, рамка текстового слоя изменилась, а рамка пользовательского представления - нет.

Код

Это мой пользовательский класс меток:

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
}

Что еще мне нужно сделать?

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()

    // ...
}

В документации сказано

Вызывайте это, когда что-то меняется в вашем пользовательском представлении, которое делает недействительным его собственный размер контента. Это позволяет системе макетов на основе ограничений учитывать новый размер внутреннего содержимого на следующем этапе макета.

Где я нашел это решение:

Другие вопросы по тегам