Как изменить ширину UITableView с анимацией? - PullRequest
2 голосов
/ 17 мая 2019

У меня UITableView слева и обычный UIView справа на моем UIViewController

  • UITableView подключается к .top, .leading и .bottom из superview
  • UIView подключается к .top, .trailing и .bottom из superview, также имеет .width
  • И UITableView .leading == .trailing из UIView

Все эти ограничения вы можете увидеть на скриншоте:

enter image description here

Вот мой код анимации в контроллере, как вы можете видеть, я обновляю .width из UIView, а также циклически повторяю анимацию.

class ViewController: UIViewController {

    @IBOutlet var widthConstaint: NSLayoutConstraint!
    @IBOutlet var tableView: UITableView!
    @IBOutlet var watchContainerView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()
        start()
    }

    func start() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            if self.widthConstaint.constant == 50 {
                self.change(width: 100)
            } else {
                self.change(width: 50)
            }
            self.start()
        }
    }

    func change(width: CGFloat) {
        widthConstaint.constant = width
        UIView.animate(withDuration: 1.0) {
            self.view.layoutIfNeeded()
        }   
    }
}

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {   
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        return cell
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 300
    }

}

В результате вы можете увидеть это на .gif, чтоЯ должен за плавную анимацию, без этого глюка?

enter image description here

Ответы [ 4 ]

4 голосов
/ 20 мая 2019

ОБНОВЛЕНИЕ

Я пытался воспроизвести 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

Но если я изменю contentMode на left (значение по умолчанию scaleToFill):

enter image description here

Синий квадрат прыгает с одного места на другое.

Итак, я обнаружил, что это не толькосвязанных с 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, изменится только позиция метки, а не ее содержимое.

1 голос
/ 21 мая 2019

Как уже упоминалось в ответе @ Gaétanz . Ненормальное поведение происходит из-за ярлыка внутри клетки.

Вы можете устранить глюки, изменив отношение начальных ограничений надписей на> = и изменив приоритет на 999 с 1000.

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

enter image description here

Когда вы добавляете ограничение с приоритетом меньше 1000, это будет необязательное ограничение. Приоритет 1000 является обязательным приоритетом для ограничения. Здесь, если вы добавили приоритет 999, все остальные ограничения с приоритетом 1000 будут размещаться первыми, а ограничение с меньшим приоритетом будет размещаться последними.

0 голосов
/ 17 мая 2019

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

func change(width: CGFloat) {
        UIView.animate(withDuration: 1.0) {
            widthConstaint.constant = width
            self.tableView.layoutIfNeeded()
            self.view.layoutIfNeeded()
        }   
    }
0 голосов
/ 17 мая 2019

Вы изменяете ширину вида сбоку за пределами блока анимации.поместите вас widthConstaint.constant = width в блок анимации.

func change(width: CGFloat) {
      UIView.animate(withDuration: 1.0) {
            self.widthConstaint.constant = width
            self.view.layoutIfNeeded()
        }   
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...