UIStackView Ẩn View Animation


82

Trong iOS 11, hành vi ẩn hoạt ảnh trong a UIStackViewđã thay đổi, nhưng tôi không thể tìm thấy tài liệu này ở bất kỳ đâu.

iOS 10

Hoạt ảnh iOS 10

iOS 11

Hoạt ảnh iOS 11

Mã trong cả hai là:

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

Làm cách nào để khôi phục hành vi trước đó trên iOS 11?

Câu trả lời:


131

Chỉ có cùng một vấn đề. Cách khắc phục là thêm vào stackView.layoutIfNeeded()bên trong khối hoạt ảnh. Nơi stackViewchứa những món đồ bạn muốn giấu.

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)

Không chắc tại sao điều này lại đột nhiên xuất hiện trong iOS 11 nhưng công bằng mà nói, đó luôn là cách tiếp cận được khuyến nghị.


1
Bạn là một anh hùng: D
Infinity James

5
Tên phù hợp cũng như 'Springham' 😆
Infinity James

3
Trong iOS <= 10 đã có một lỗi trong đó thiết lập các hiddenthuộc tính của một UIStackView's subviewtrong khối hoạt hình đã được bỏ qua trong một số trường hợp, vì vậy cách tốt nhất là để thay đổi nó bên ngoài của nó, ngay trước khi các hình ảnh động.
Iulian Onofrei.

2
Có thể là một sự hiểu lầm từ phía tôi nhưng nó không có âm thanh từ tài liệu như view.layoutIfNeeded()sẽ cập nhật vị trí của các chế độ xem khác trong StackView, đó là những gì chúng tôi muốn. developer.apple.com/documentation/uikit/uiview/…
A Springham

5
view.layoutIfNeeded () là ok, tuy nhiên việc gọi view.isHidden = true nếu view đã bị ẩn (hoặc ngược lại) sẽ phá vỡ điều. Vì vậy, hãy nhớ kiểm tra xem chế độ xem chưa phải là trạng thái ẩn mà bạn muốn thay đổi. if (view.isHidden == true) {view.isHidden = false}
glemoulant

5

Tiện ích mở rộng 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
Đối với tôi, cách khắc phục là kiểm tra self.isHiddenvà không đặt giá trị nếu đã giống nhau.
richy

1
đó có thể dễ dàng là 1 hàm được gọi là toggleAnimated (in ..., show: Bool). vì chỉ một dòng thay đổi :) cộng với nó không hoạt động với tôi: s
Jean Raymond Daher

Có, 2 hàm sẽ là đường cú pháp sau khi tạo một hàm duy nhất
ergunkocak

4

Nó đã được đề cập trong các nhận xét của câu trả lời được chấp nhận, nhưng đây là vấn đề của tôi và nó không có trong bất kỳ câu trả lời nào ở đây nên:

Đảm bảo không bao giờ đặt isHidden = truetrên một chế độ xem đã bị ẩn. Điều này sẽ làm xáo trộn chế độ xem ngăn xếp.


Đây là vấn đề của tôi, và tôi không cần gọi layoutIfNeedednên tôi tự hỏi liệu đây có phải là câu trả lời chính xác hay không.
B Roy Dawson

3

Tôi muốn chia sẻ chức năng này tốt cho việc ẩn và hiển thị nhiều chế độ xem UIStackView, bởi vì với tất cả các mã tôi đã sử dụng trước đây không hoạt động trơn tru vì người ta cần phải loại bỏAnimation khỏi một số lớp:

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

Điều này cũng giải quyết vấn đề lượt xem không bị mờ dần trong / ngoài. Xinh đẹp!
Petr Fiala

2

Tiện ích mở rộng để ẩn / hiện các phần tử đơn lẻ

Nó không liên quan 100%, nhưng nếu bạn đang tìm một cách ngắn gọn để ẩn UIViewcác phần tử của đơn lẻ (trong chế độ xem ngăn xếp hoặc bất kỳ nơi nào khác), bạn có thể sử dụng tiện ích mở rộng đơn giản này mà tôi đã tạo:

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

Tôi sử dụng nó để ẩn / hiển thị các phần tử có hoạt ảnh một cách thuận tiện trong chế độ xem ngăn xếp với một dòng mã. Thí dụ:

validatableButton.isHiddenAnimated(value: false)

0

Hy vọng điều này sẽ tiết kiệm cho những người khác một vài giờ thất vọng.

Ẩn hoạt ảnh VÀ hiển thị nhiều lần xem phụ UIStackView cùng một lúc là một mớ hỗn độn.

Trong một số trường hợp, các thay đổi .isHidden trong các khối hoạt ảnh hiển thị chính xác cho đến khi hoạt ảnh tiếp theo, thì .isHidden bị bỏ qua. Mẹo đáng tin cậy duy nhất mà tôi tìm thấy cho việc này là lặp lại hướng dẫn .isHidden trong phần hoàn thành của khối hoạt ảnh.

    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
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.