W iOS 11 UIStackView
zmieniło się zachowanie animacji ukrywania w a , ale nigdzie nie mogłem znaleźć tego udokumentowanego.
iOS 10
iOS 11
Kod w obu jest następujący:
UIView.animate(withDuration: DiscoverHeaderView.animationDuration,
delay: 0.0,
usingSpringWithDamping: 0.9,
initialSpringVelocity: 1,
options: [],
animations: {
clear.isHidden = hideClear
useMyLocation.isHidden = hideLocation
},
completion: nil)
Jak przywrócić poprzednie zachowanie w iOS 11?
hidden
polegający na tym, że ustawienie właściwościUIStackView
asubview
w bloku animacji było w niektórych przypadkach ignorowane, więc najlepiej jest zmienić to poza nim, tuż przed animacją.view.layoutIfNeeded()
zaktualizowała pozycję innych widoków w StackView, czego chcemy. developer.apple.com/documentation/uikit/uiview/…Rozszerzenie 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 ) } } }
źródło
self.isHidden
i nie ustawianiu wartości, jeśli jest już taka sama.Wspomniano już o tym w komentarzach do zaakceptowanej odpowiedzi, ale to był mój problem i nie ma go w żadnej z odpowiedzi, więc:
Upewnij się, że nigdy nie włączasz
isHidden = true
widoku, który jest już ukryty. Spowoduje to zepsucie widoku stosu.źródło
layoutIfNeeded
więc zastanawiam się, czy to powinna być prawidłowa odpowiedź.Chcę udostępnić tę funkcję, która jest dobra do ukrywania i pokazywania wielu widoków
UIStackView
, ponieważ cały kod, którego użyłem wcześniej, nie działał płynnie, ponieważ trzeba usunąć animację z niektórych warstw: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) } } }
źródło
Rozszerzenie do ukrywania / pokazywania pojedynczych elementów
Nie jest to w 100% powiązane, ale jeśli szukasz zwięzłego sposobu na ukrycie
UIView
elementów singla (w widoku stosu lub gdziekolwiek indziej), możesz użyć tego prostego rozszerzenia, które zrobiłem:extension UIView { func isHiddenAnimated(value: Bool, duration: Double = 0.2) { UIView.animate(withDuration: duration) { [weak self] in self?.isHidden = value } } }
Używam go do wygodnego ukrywania / pokazywania elementów z animacją w widoku stosu z pojedynczym wierszem kodu. Przykład:
validatableButton.isHiddenAnimated(value: false)
źródło
Mam nadzieję, że zaoszczędzi to innym kilku godzin frustracji.
Animowanie ukrywania ORAZ wyświetlania wielu podglądów podrzędnych UIStackView w tym samym czasie to bałagan.
W niektórych przypadkach zmiany .isHidden w blokach animacji są wyświetlane poprawnie do następnej animacji, a następnie .isHidden jest ignorowane. Jedyną niezawodną sztuczką, jaką znalazłem, jest powtórzenie instrukcji .isHidden w sekcji uzupełniania bloku animacji.
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 }
źródło