ОБНОВЛЕНИЕ
Я пытался воспроизвести UILabel
неправильное поведение с моим собственным UIView
подклассом:
class MyView: UIView {
var currentSize: CGSize = .zero
override func layoutSubviews() {
super.layoutSubviews()
guard currentSize != bounds.size else { return }
currentSize = bounds.size
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
UIColor.purple.setFill()
UIBezierPath(rect: rect).fill()
let square = CGRect(x: rect.width - 50, y: rect.origin.y, width: 50, height: rect.height)
UIColor.blue.setFill()
UIBezierPath(rect: square).fill()
}
}
Представление рисует маленький синий прямоугольниксправа от него, как текст.И ... ну ... анимация тоже хороша!
![enter image description here](https://i.stack.imgur.com/Nasec.gif)
Но если я изменю contentMode
на left
(значение по умолчанию scaleToFill
):
![enter image description here](https://i.stack.imgur.com/CXRVd.gif)
Синий квадрат прыгает с одного места на другое.
Итак, я обнаружил, что это не толькосвязанных с UILabel
* textAlignment
.Это его сочетание со стандартным left
режимом содержимого UILabel
.
contentMode
определяет, как должен корректироваться контент, если размер контента и размер границ различаются - это имеет место во время анимациивид перерисовывается с новым размером прямо перед изменением размера.
Таким образом
label.contentMode = .left // default
label.textAlignment = .left
будет вести себя как:
label.contentMode = .right
label.textAlignment = .right
ПРЕДЫДУЩИЙ ОТВЕТ
Я потратил некоторое время на вашу проблему.Угадай, что?
Это никак не связано с табличным представлением.Это из-за выравнивания текста .right
на этикетке.
Вы можете воспроизвести глюк простым UILabel
:
class ViewController: UIViewController {
let label = UILabel()
var rightConstraint: NSLayoutConstraint!
override func loadView() {
view = UIView()
view.backgroundColor = .white
}
override func viewDidLoad() {
super.viewDidLoad()
setUpView()
start()
}
func start() {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
if self.rightConstraint.constant == 50 {
self.change(width: 100)
} else {
self.change(width: 50)
}
self.start()
}
}
func change(width: CGFloat) {
rightConstraint.constant = width
UIView.animate(withDuration: 1.0) {
self.view.layoutIfNeeded()
}
}
func setUpView() {
label.text = "Label"
label.textAlignment = .right
view.addSubview(label)
label.translatesAutoresizingMaskIntoConstraints = false
rightConstraint = view.trailingAnchor.constraint(equalTo: label.trailingAnchor)
rightConstraint.isActive = true
label.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
label.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
label.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
}
}
Почему?
Чтобы выяснить ошибку, я создал подкласс UILabel
и переопределил action(for: CALayer, forKey: String)
.Именно здесь каждый UIView
решает, какую анимацию применять в ответ на изменение свойства ( ref ).Я искал странное поведение анимации, когда метка находится внутри табличного представления.
Если вы напечатаете key
, вы увидите, что действия запрашиваются для позиции, границ и содержимого метки.когда UIKit
собирается передать анимацию, связанную с вашим вызовом UIView.animate(withDuration:)
.
Фактически, setNeedsDisplay
вызывается каждый раз, когда изменяется размер UILabel
(это имеет смысл).Поэтому я полагаю, что существует конфликт между перерисовкой содержимого метки и сменой фрейма.
Я думаю, что вам следует воссоздать правильное выравнивание текста метки без использования textAlignment = .right
.
Горизонтально, наклейте этикетку только вправо.Когда будет вызван layoutIfNeeded
, изменится только позиция метки, а не ее содержимое.