UIStackView Hide View Animation
Just had the same issue.
The fix is adding stackView.layoutIfNeeded()
inside the animation block. Where stackView
is the container of the items you're wishing to hide.
UIView.animate(withDuration: DiscoverHeaderView.animationDuration,
delay: 0.0,
usingSpringWithDamping: 0.9,
initialSpringVelocity: 1,
options: [],
animations: {
clear.isHidden = hideClear
useMyLocation.isHidden = hideLocation
stackView.layoutIfNeeded()
},
completion: nil)
Not sure why this is suddenly an issue in iOS 11 but to be fair it has always been the recommended approach.
It's already mentioned in the comments of the accepted answer, but this was my problem and it's not in any of the answer here so:
Make sure to never set isHidden = true
on a view that is already hidden. This will mess up the stack view.
Swift 4 Extension:
// 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
)
}
}
}
I want to share this function that is good for hiding and showing many views in UIStackView
, because with all the code I used before didn't work smooth because one needs to removeAnimation from some layers:
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)
}
}
}