Как сделать так, чтобы рамка пользовательского представления соответствовала своему внутреннему размеру контента
Пользовательская метка, которая изменяет размеры в соответствии с ее содержанием
Я делаю этикетку с нуля. (Моя конечная цель - сделать вертикальную текстовую метку для монгольского шрифта, но сейчас я просто делаю обычную горизонтальную текстовую метку на практике.)
Пока нет ограничений по длине и ширине, я хочу, чтобы размер рамки моей пользовательской метки изменялся в соответствии с ее внутренним размером содержимого.
Работает в 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()
// ...
}
В документации сказано
Вызывайте это, когда что-то меняется в вашем пользовательском представлении, которое делает недействительным его собственный размер контента. Это позволяет системе макетов на основе ограничений учитывать новый размер внутреннего содержимого на следующем этапе макета.
Где я нашел это решение: