UIStackView Hide View Animation


83

У iOS 11 поведінка прихованої анімації в 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?

Відповіді:


135

Просто була та сама проблема. Виправлення додається 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
Infinity James

5
Відповідне ім'я також 'Springham' 😆
Infinity James

4
У прошивкою <= 10 була помилка , в якій установка hiddenвластивості UIStackViewsubviewв блоці анімації була ігноруються в деяких випадках, так що кращий спосіб , щоб змінити його за його межами, прямо перед анімацією.
Юліан Онофрей,

2
Можливо, з мого боку є непорозуміння, але з документації це не звучить, як view.layoutIfNeeded()би оновлення позиції інших поглядів у StackView, саме цього ми і хочемо. developer.apple.com/documentation/uikit/uiview/…
А Спрінгхем,

6
view.layoutIfNeeded () - це нормально, проте виклик 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а не встановити значення, якщо є, вже однакове.
Річі

1
це легко може бути 1 функція, що називається toggleAnimated (у ..., show: Bool). оскільки змінюється лише один рядок :) плюс це у мене не спрацювало: s
Jean Raymond Daher

Так, 2 функції були б синтаксичним цукром після створення єдиної функції
ergunkocak

4

Це вже згадувалося в коментарях до прийнятої відповіді, але це була моя проблема, і ні в одній з відповідей тут немає, тому:

Переконайтеся, що ніколи не встановлюєте isHidden = trueвигляд, який уже прихований. Це зіпсує вигляд стека.


Це була моя проблема, і мені не потрібно було телефонувати, layoutIfNeededтому я думаю, чи це повинна бути правильна відповідь.
B Roy Dawson

3

Я хочу поділитися цією функцією, яка добре приховує та показує багато переглядів UIStackView, оскільки з усім кодом, який я використовував раніше, не працював гладко, оскільки потрібно видалити Анімацію з деяких шарів:

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

Я використовую його, щоб зручно приховувати / показувати елементи з анімацією у вигляді стека з одним рядком коду. Приклад:

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
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.