UIStackView 숨기기보기 애니메이션


82

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에서 이전 동작을 어떻게 복원합니까?

답변:


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
아이폰 OS <상기 설정되는 버그 = 10 있었다 hidden(A)의 특성 UIStackView의를 subview최적의 방식으로 바로 애니메이션 전에 외부 그것을 변경하도록하므로 애니메이션 블록은 경우에 따라 무시 하였다.
이 울리 Onofrei

2
내 부분에 대한 오해 일 수 있지만 view.layoutIfNeeded()우리가 원하는 StackView의 다른보기 위치를 업데이트하는 것과 같은 문서에서는 들리지 않습니다 . developer.apple.com/documentation/uikit/uiview/…
A Springham

5
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은 이미 동일한 경우 값 을 확인 하고 설정하지 않는 것입니다.
richy

1
이것은 toggleAnimated (in ..., show : Bool)이라는 하나의 함수가 될 수 있습니다. 한 줄만 변경 되었기 때문에 :) 게다가 그것은 나를 위해 작동하지
않았습니다

예, 단일 함수를 만든 후 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)
        }
    }
}

이것은 또한 뷰가 페이드 인 / 아웃되지 않는 문제를 해결했습니다. 아름다운!
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
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.