CBasicAnimation не надежен в UIStackView?

У меня есть UIStackView с вертикальной осью, которая отображает некоторые элементы, похожие на UITableView. Однако я анимирую их в режиме Delay с помощью DispatchQueue.main.asyncAfter.

Вот код, который я использую для создания CABasicAnimation:

let anim = CABasicAnimation(keyPath: "transform.translation.x")
anim.duration = 0.2
anim.fromValue = UIScreen.main.bounds.size.width
anim.toValue = 0
anim.isRemovedOnCompletion = true
anim.fillMode = kCAFillModeRemoved

По какой-то причине, по крайней мере, в Симуляторе (я не думаю, что это имеет значение, так как это приложение должно работать на старых устройствах или мощных устройствах, таких как iPhone 7+), анимация не всегда приземляется на одно и то же место.

Иногда ячейка прилетает и находится примерно в 8 пикселях слева, иногда в 12, иногда в 20. Это кажется непостоянным. Но я думаю, что мое toValue равно 0, оно должно появиться там, где оно обычно появляется, если бы я не добавлял анимацию к слою.

Я попытался установить для isRemovedOnCompletion значение true, а для fillMode удалить, но это, похоже, не исправило анимацию.

Кстати, по мере продолжения анимации есть один момент, когда ячейка создается из горизонтального UIStackView, где в ней скрыты аранжированные подвиды. Я использую метод UIView.animateWithDuration, чтобы установить для их скрытых значений значение false и, таким образом, анимировать подпредставления.

Причина, по которой я это замечаю, заключается в том, что, когда это происходит, ячейки вверху возвращаются на свои места, а не на несколько пикселей.

Я попробовал setNeedsDisplay для ячеек после их анимации, а также для вертикального стека, но это тоже не сработало. Я не понимаю, что происходит и почему эти слои просто не анимируются в правильном положении. Любые идеи?

P.S. Весь вертикальный stackView инициализируется в методе инициализации ViewController. Анимации происходят в методе viewDidAppear. Я попытался компенсировать анимацию, добавив DispatchQueue.main.asyncAfter, а также первым делом после super.viewDidAppear(animated), но оба результата были одинаковыми.


person Kjell    schedule 25.01.2017    source источник
comment
Вы, вероятно, были бы более успешными, анимируя ограничения, а не пытаясь преобразовать местоположение X. Представления стека широко используют автомакет, и вы эффективно работаете вне этой системы.   -  person Paulw11    schedule 25.01.2017
comment
Я попытался отредактировать константу внутри раскадровки ведущего значения UIView, где UIView находится внутри UIStackView. Это не нравится. Как мне анимировать ограничения для положения, когда стек не позволяет этого? Может быть, мне нужно создать ячейку-заполнитель. А потом анимировать содержимое?   -  person Kjell    schedule 25.01.2017
comment
Да, добавил заполнитель, и это исправило.   -  person Kjell    schedule 26.01.2017
comment
Да, я собирался предложить это; Представления стека довольно умны, но с ними нужно работать; они пытаются вычислить внутренний размер своего контента, поэтому они плохо работают, когда контент пуст. Заполнитель исправляет это.   -  person Paulw11    schedule 26.01.2017


Ответы (2)


Анимация элементов в виде стека и из него на самом деле ОЧЕНЬ проста.

Все, что вам нужно сделать, это установить рамку представления в то место, где вы хотите, чтобы оно начиналось, прежде чем добавлять его в представление стека, а затем вызвать layoutIfNeeded() внутри блока анимации сразу после добавления нового представления. Вот пример кода из работающего приложения:

let newView = createNewView()
self.stackView.addArrangedSubview(newView)
if sender != nil {
  UIView.animate(withDuration: 0.2) {
    self.stackView.layoutIfNeeded()
  }
}
person Duncan C    schedule 25.01.2017
comment
Итак, если я хочу, чтобы все ячейки вылетали слева, мне нужно установить их рамку в положение, которое было бы после добавления в качестве аранжированного подпредставления, но минус ширина экрана для xPosition? Это кажется немного сложным, так как теперь мне нужно это вычислить. И если это так, то почему я использую stackview для начала, если не для упрощения всех вычислений? - person Kjell; 25.01.2017
comment
Это было бы довольно хорошим решением в ситуации, когда я добавлял новые аранжированные подпредставления для каждого взаимодействия с пользователем, и я хотел, чтобы они исходили из рассчитанной мной позиции, а не позволяли вычислить ее UIStackView. Например, когда пользователь нажимает кнопку «плюс», и золотые монеты оживают по одной из мешка с золотом или что-то в этом роде. - person Kjell; 26.01.2017
comment
Если вы хотите, чтобы они всегда отображались слева, установите для их позиции x значение 0 или даже -tileView.bounds.width, а для значения y установите значение 0. Как говорит Пол, вы боретесь с автоматическим расположением, которое делает ваш представления стека работают. - person Duncan C; 26.01.2017

Разобрался с вопросом. Вот проблема...

1) Вы хотите анимировать только уровень представления, но сохранить фактическое положение элементов из-за того, что UIStackView хорошо позиционирует вас. Так что CoreAnimation кажется отличным вариантом.

2) Поскольку вы имеете дело с UIStackView, на первый взгляд, вы не можете анимировать ограничения положения для его аранжированных подпредставлений. Так что это еще одна причина, по которой вы не хотите анимировать ограничения и использовать CoreAnimation.

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

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

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

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

Установите ограничения для подпредставлений этого представления-заполнителя по отношению к этому представлению-заполнителю. Теперь вы можете анимировать эти ограничения с помощью обычных инструментов анимации AutoLayout. Например:

constraint.constant = 0
UIView.animate(withDuration: 0.2, 
    delay: 0.1, 
    options: UIViewAnimationOptions.curveEaseOut, 
    animations: {
        cell.layoutIfNeeded()
    }, completion: nil)

Буя! Анимации плавные и на самом деле заканчиваются там, где вы хотите, относительно аранжированного подвида. Ууууу!

person Kjell    schedule 25.01.2017