UIStackView隐藏视图动画


82

在iOS 11中,a中隐藏动画的行为UIStackView已更改,但是我无法在任何地方找到该文档。

iOS 10

iOS 10动画

iOS 11

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上还原以前的行为?

Answers:


131

只是有同样的问题。该修复程序将添加stackView.layoutIfNeeded()到动画块中。stackView您要隐藏的物品的容器在哪里?

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)

不确定为什么这会突然成为iOS 11中的问题,但公平地说,这一直是推荐的方法。


1
您是英雄:D
无限詹姆斯

5
适当的名称,以及“Springham”😆
无限詹姆斯

3
在IOS <= 10有在其中设置的一个错误hidden一个的属性UIStackViewsubview动画块正在在某些情况下被忽略,所以最好的方法是将它的外部改变它,动画之前。
Iulian Onofrei

2
在我看来,这可能是一个误解,但听起来并没有像view.layoutIfNeeded()我们想要的那样在StackView中更新其他视图的位置。developer.apple.com/documentation/uikit/uiview/...
一个Springham

5
view.layoutIfNeeded()可以,但是如果view已经被隐藏(或相反),则调用view.isHidden = true可以解决问题。因此,请确保检查视图是否尚未变为您要更改的隐藏状态。if(view.isHidden == true){view.isHidden = false}
glemoulant

5

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
        )
    }
}
}

5
对我来说,解决方法是检查self.isHidden并且不设置值(如果已经相同)。
richy

1
这很容易就是1个名为toggleAnimated(in ...,show:Bool)的函数。因为只有一行更改:)加上对我不起作用:s
Jean Raymond Daher

是的,在制作单个函数后,两个函数将成为语法糖
ergunkocak,

4

可接受答案的注释中已经提到了它,但这是我的问题,这里没有任何答案,因此:

确保永远不要isHidden = true在已经隐藏的视图上进行设置。这会弄乱堆栈视图。


这是我的问题,我不需要打电话,layoutIfNeeded所以我想知道这是否是正确的答案。
B罗伊道森

3

我想分享这一功能,该功能非常适合在中隐藏和显示许多视图UIStackView,因为在我之前使用的所有代码中,由于需要从某些图层中移除removeAnimation,所以无法正常工作:

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)
        }
    }
}

这也解决了视图不淡入/淡出的问题。美丽!
Petr Fiala

2

扩展以隐藏/显示单个元素

它不是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)

0

希望这可以节省别人几个小时的挫败感。

动画隐藏和同时显示多个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
    }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.