UIStackView Hide View Animation

82

In iOS 11 hat sich das Verhalten der Hide-Animation in a UIStackViewgeändert, aber ich konnte dies nirgendwo dokumentiert finden.

iOS 10

iOS 10 Animation

iOS 11

iOS 11 Animation

Der Code in beiden ist folgender:

UIView.animate(withDuration: DiscoverHeaderView.animationDuration,
                       delay: 0.0,
                       usingSpringWithDamping: 0.9,
                       initialSpringVelocity: 1,
                       options: [],
                       animations: {
                            clear.isHidden = hideClear
                            useMyLocation.isHidden = hideLocation
                        },
                       completion: nil)

Wie kann ich das vorherige Verhalten unter iOS 11 wiederherstellen?

Unendlichkeit James
quelle

Antworten:

131

Hatte gerade das gleiche Problem. Das Update wird stackView.layoutIfNeeded()im Animationsblock hinzugefügt . Wo stackViewist der Behälter mit den Gegenständen, die Sie verstecken möchten?

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)

Ich bin mir nicht sicher, warum dies plötzlich ein Problem in iOS 11 ist, aber um fair zu sein, war es immer der empfohlene Ansatz.

Ein Springham
quelle
1
Du bist ein Held: D
Infinity James
5
Passender Name auch 'Springham'
Infinity James
3
In iOS <= 10 gibt es einen Fehler , bei dem der Einstellung der hiddenEigenschaft eines UIStackView‚s subviewin der Animation Block wurde in einigen Fällen ignoriert wird, so dass der beste Weg , um es außerhalb davon zu ändern , ist, kurz vor der Animation.
Iulian Onofrei
2
Könnte ein Missverständnis meinerseits sein, aber es klingt nicht so, als view.layoutIfNeeded()würde es die Position anderer Ansichten in der StackView aktualisieren, was wir wollen. developer.apple.com/documentation/uikit/uiview/…
Ein Springham
5
view.layoutIfNeeded () ist in Ordnung, aber das Aufrufen von view.isHidden = true, wenn view bereits ausgeblendet ist (oder umgekehrt), bricht das Ding. Stellen Sie daher sicher, dass Sie überprüfen, ob die Ansicht noch nicht der verborgene Status ist, den Sie ändern möchten. if (view.isHidden == true) {view.isHidden = false}
glemoulant
5

Swift 4-Erweiterung:

// 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
        )
    }
}
}
Ergunkocak
quelle
5
Für mich bestand die Lösung darin, self.isHiddenden Wert zu überprüfen und nicht festzulegen, wenn er bereits gleich ist.
Richy
1
Das könnte leicht eine Funktion sein, die toggleAnimated heißt (in ..., show: Bool). da sich nur eine Zeile ändert :) plus es hat bei mir nicht funktioniert: s
Jean Raymond Daher
Ja, 2 Funktionen wären syntaktischer Zucker nach der Herstellung einer einzigen Funktion
Ergunkocak
4

Es ist bereits in den Kommentaren der akzeptierten Antwort erwähnt, aber dies war mein Problem und es ist in keiner der Antworten hier so:

Stellen Sie sicher, dass Sie niemalsisHidden = true eine Ansicht festlegen , die bereits ausgeblendet ist. Dies wird die Stapelansicht durcheinander bringen.

jimpic
quelle
Dies war mein Problem, und ich musste nicht anrufen, layoutIfNeededalso frage ich mich, ob dies die richtige Antwort sein sollte.
B Roy Dawson
3

Ich möchte diese Funktion teilen, die sich gut zum Ausblenden und Anzeigen vieler Ansichten eignet UIStackView, da der gesamte zuvor verwendete Code nicht reibungslos funktioniert hat, da Animation aus einigen Ebenen entfernt werden muss:

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)
        }
    }
}
Paul T.
quelle
Dies löste auch das Problem, dass Ansichten nicht ein- und ausgeblendet wurden. Wunderschönen!
Petr Fiala
2

Erweiterung zum Ausblenden / Anzeigen einzelner Elemente

Es ist nicht zu 100% verwandt, aber wenn Sie nach einer präzisen Möglichkeit suchen, einzelne UIViewElemente auszublenden (entweder in einer Stapelansicht oder irgendwo anders), können Sie diese einfache Erweiterung verwenden, die ich erstellt habe:

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

Ich verwende es, um Elemente mit Animation in einer Stapelansicht mit einer einzelnen Codezeile bequem auszublenden / anzuzeigen. Beispiel:

validatableButton.isHiddenAnimated(value: false)
Alessandro Francucci
quelle
0

Hoffe, das erspart anderen ein paar Stunden Frust.

Das gleichzeitige Ausblenden UND Anzeigen mehrerer UIStackView-Unteransichten zu animieren, ist ein Chaos.

In einigen Fällen werden die .isHidden-Änderungen in den Animationsblöcken bis zur nächsten Animation korrekt angezeigt. Dann wird .isHidden ignoriert. Der einzige zuverlässige Trick, den ich dafür gefunden habe, besteht darin, die .isHidden-Anweisungen im Abschlussabschnitt des Animationsblocks zu wiederholen.

    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
    }
Matjan
quelle