В iOS 11 поведение анимации скрытия внутри a UIStackView
изменилось, но я нигде не смог найти это задокументировано.
iOS 10
iOS 11
Код в обоих случаях таков:
UIView.animate(withDuration: DiscoverHeaderView.animationDuration,
delay: 0.0,
usingSpringWithDamping: 0.9,
initialSpringVelocity: 1,
options: [],
animations: {
clear.isHidden = hideClear
useMyLocation.isHidden = hideLocation
},
completion: nil)
Как восстановить прежнее поведение на iOS 11?
hidden
свойстваUIStackView
-йsubview
в блоке анимации была игнорируются в некоторых случаях, так что лучший способ , чтобы изменить его за его пределами, прямо перед анимацией.view.layoutIfNeeded()
Возможно, я ошибаюсь, но это не звучит из документов, как будто бы обновили положение других представлений в StackView, чего мы и хотим. developer.apple.com/documentation/uikit/uiview/…Расширение Swift 4:
// MARK: - Show hide animations in StackViews extension UIView { func hideAnimated(in stackView: UIStackView) { if !self.isHidden { UIView.animate( withDuration: 0.35, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 1, options: [], animations: { self.isHidden = true stackView.layoutIfNeeded() }, completion: nil ) } } func showAnimated(in stackView: UIStackView) { if self.isHidden { UIView.animate( withDuration: 0.35, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 1, options: [], animations: { self.isHidden = false stackView.layoutIfNeeded() }, completion: nil ) } } }
источник
self.isHidden
а не устанавливать значение, если оно уже такое же.Это уже упоминалось в комментариях к принятому ответу, но это была моя проблема, и ее нет ни в одном из ответов здесь, поэтому:
Убедитесь в том , чтобы никогда не установить
isHidden = true
на вид , что уже скрыты. Это испортит представление стека.источник
layoutIfNeeded
поэтому мне интересно, должен ли это быть правильный ответ.Я хочу поделиться этой функцией, которая хороша для скрытия и отображения множества представлений
UIStackView
, потому что весь код, который я использовал раньше, не работал гладко, потому что нужно удалить анимацию из некоторых слоев:extension UIStackView { public func make(viewsHidden: [UIView], viewsVisible: [UIView], animated: Bool) { let viewsHidden = viewsHidden.filter({ $0.superview === self }) let viewsVisible = viewsVisible.filter({ $0.superview === self }) let blockToSetVisibility: ([UIView], _ hidden: Bool) -> Void = { views, hidden in views.forEach({ $0.isHidden = hidden }) } // need for smooth animation let blockToSetAlphaForSubviewsOf: ([UIView], _ alpha: CGFloat) -> Void = { views, alpha in views.forEach({ view in view.subviews.forEach({ $0.alpha = alpha }) }) } if !animated { blockToSetVisibility(viewsHidden, true) blockToSetVisibility(viewsVisible, false) blockToSetAlphaForSubviewsOf(viewsHidden, 1) blockToSetAlphaForSubviewsOf(viewsVisible, 1) } else { // update hidden values of all views // without that animation doesn't go let allViews = viewsHidden + viewsVisible self.layer.removeAllAnimations() allViews.forEach { view in let oldHiddenValue = view.isHidden view.layer.removeAllAnimations() view.layer.isHidden = oldHiddenValue } UIView.animate(withDuration: 0.3, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 1, options: [], animations: { blockToSetAlphaForSubviewsOf(viewsVisible, 1) blockToSetAlphaForSubviewsOf(viewsHidden, 0) blockToSetVisibility(viewsHidden, true) blockToSetVisibility(viewsVisible, false) self.layoutIfNeeded() }, completion: nil) } } }
источник
Расширение для скрытия / отображения отдельных элементов
Это не на 100% связано, но если вы ищете краткий способ скрыть отдельные
UIView
элементы (либо в виде стека, либо где-либо еще), вы можете использовать это простое расширение, которое я сделал:extension UIView { func isHiddenAnimated(value: Bool, duration: Double = 0.2) { UIView.animate(withDuration: duration) { [weak self] in self?.isHidden = value } } }
Я использую его для удобного скрытия / отображения элементов с анимацией в виде стека с помощью одной строчки кода. Пример:
validatableButton.isHiddenAnimated(value: false)
источник
Надеюсь, это избавит других от нескольких часов разочарования.
Анимировать скрытие И одновременно показывать несколько подвидов UIStackView - это беспорядок.
В некоторых случаях изменения .isHidden в блоках анимации отображаются правильно до следующей анимации, затем .isHidden игнорируется. Единственный надежный прием, который я нашел для этого, - это повторение инструкций .isHidden в разделе завершения блока анимации.
let time = 0.3 UIView.animate(withDuration: time, animations: { //shows self.googleSignInView.isHidden = false self.googleSignInView.alpha = 1 self.registerView.isHidden = false self.registerView.alpha = 1 //hides self.usernameView.isHidden = true self.usernameView.alpha = 0 self.passwordView.isHidden = true self.passwordView.alpha = 0 self.stackView.layoutIfNeeded() }) { (finished) in self.googleSignInView.isHidden = false self.registerView.isHidden = false self.usernameView.isHidden = true self.passwordView.isHidden = true }
источник