UIStackViewビューアニメーションを非表示


83

iOS 11では、内の非表示アニメーションの動作 UIStackViewが変更されましたが、これが文書化されている場所を見つけることができませんでした。

iOS 10

iOS10アニメーション

iOS 11

iOS11アニメーション

両方のコードは次のとおりです。

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で以前の動作を復元するにはどうすればよいですか?

回答:


133

ちょうど同じ問題がありました。修正は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)

これがiOS11で突然問題になる理由はわかりませんが、公平を期すために、常に推奨されるアプローチでした。


1
あなたはヒーローです:D
インフィニティジェームズ

5
適切な名前も「Springham」😆–
Infinity James

4
iOS <= 10では、アニメーションブロックhiddenでのUIStackView'sのプロパティの設定subviewが無視される場合があるというバグがあったため、アニメーションの直前に、その外で変更するのが最善の方法です。
Iulian Onofrei 2017年

2
私の側では誤解かもしれませんview.layoutIfNeeded()が、StackView内の他のビューの位置を更新するようなドキュメントからは聞こえません。developer.apple.com/documentation/uikit/uiview/...
A Springham

6
view.layoutIfNeeded()は問題ありませんが、ビューがすでに非表示になっている(またはその逆の)場合にview.isHidden = trueを呼び出すと、問題が発生します。したがって、ビューが変更したい非表示の状態になっていないかどうかを必ず確認してください。if(view.isHidden == true){view.isHidden = false}
華やかな

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、値がすでに同じであるかどうかを確認し、値を設定しないことでした。
リッチな2018

1
これは、toggleAnimated(in ...、show:Bool)と呼ばれる1つの関数である可能性があります。1行しか変更されていないので:)それに加えて、私には機能しませんでした:s
Jean RaymondDaher20年

はい、2つの機能は、単一のFUNCを行った後、糖衣構文だろう
ergunkocak

4

受け入れられた回答のコメントですでに言及されていますが、これは私の問題であり、ここではどの回答にも含まれていません。

すでに非表示になっているビューには絶対に設定しないでくださいisHidden = true。これにより、スタックビューが台無しになります。


これが私の問題で、電話layoutIfNeededする必要がなかったので、これが正解かどうか疑問に思います。
B RoyDawson20年

3

で多くのビューを表示および非表示にするのに適したこの関数を共有したいと思います。これはUIStackView、以前に使用したすべてのコードで、一部のレイヤーからAnimationを削除する必要があるため、スムーズに機能しなかったためです。

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

これにより、ビューがフェードイン/フェードアウトしないという問題も解決されました。綺麗な!
ペトルフィアラ

3

単一の要素を非表示/表示する拡張機能

これは100%関連しているわけではありませんが、単一UIViewの要素を(スタックビューまたはその他の場所で)非表示にする簡潔な方法を探している場合は、私が作成したこの単純な拡張機能を使用できます。

extension UIView {
    func isHiddenAnimated(value: Bool, duration: Double = 0.2) {
        UIView.animate(withDuration: duration) { [weak self] in self?.isHidden = value }
    }
}

これを使用して、1行のコードでスタックビューにアニメーション付きの要素を便利に表示/非表示にします。例:

validatableButton.isHiddenAnimated(value: false)

1

これが他の人の数時間の欲求不満を救うことを願っています。

複数の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
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.