본문 바로가기

카테고리 없음

[SwiftUI] 트랜지션

트랜지션은..

뷰 계층 구조에 새로운 뷰가 추가되거나 기존에 있던 것이 제거될 때 적용되는 애니메이션의 한 종류

따라서 transition Modifier 단독으로는 동작하지 않고 animation Modifier나 withAnimation 함수와 함께 사용해야 한다.

 

- 뷰 계층 구조에 새로운 뷰가 삽입되는 과정

- 뷰 계층 구조에 이미 추가된 뷰가 제거되는 과정

- 삽입과 제거가 동시에 일어나는 과정

 

중요한 것은 <뷰 계층 구조>에 변화가 생겨야 하고,

만약 뷰는 동일하고 그 안의 내용만 바뀌는 경우는 트랜지션이 적용되지 않는다.

 

struct TransitionExampleView: View {
    
    @State private var showText = false
    
    var body: some View {
        VStack {
            if showText {
                Text("Transition")
            }
            
            Button {
                withAnimation {
                    self.showText.toggle()
                }
            } label: {
                Text("버튼 보이기")
            }
        }
    }
}

 

Text라는 View가 VStack에 삽입/제거될 때 아무런 transition도 지정해주지 않으면 어떻게 될까?

 

 

기본으로 뭔가 적용된 것을 알 수 있음. 

그래도 Text가 뽝! 하고 나오는것 같은 모습..

 

slide로 바꿔보았는데

struct TransitionExampleView: View {
    
    @State private var showText = false
    
    var body: some View {
        VStack {
            // Text가 추가/제거될 때 transition 효과 적용
            if showText {
                Text("Transition")
                    .transition(.slide) // .slide transition 설정
                    .animation(.easeInOut, value: showText)
            }
                
            Button {
                withAnimation {
                    self.showText.toggle() // 애니메이션과 함께 상태 토글
                }
            } label: {
                Text("버튼 보이기")
            }
        }
    }
}

왜 사라질 때만 적용되고 나타날 땐 적용이 안되는건지 모르겠네

 

AnyTransition

트랜지션을 다룰 때는 타입 소거된 형태의 AnyTranstion 타입을 이용해야 한다.

 

트랜지션 합성

combine

- 2개 이상의 AnyTransition을 합성할 수 있다!

Text("Transition")
	.transition(AnyTransition.slide.combined(with: .opacity))

 

asymmetric

- 기본 전환 효과들은 뷰가 삽입되고 제거될 때 동일한 형태로 적용되지만, 이걸 사용하면 두 시점에 서로 다르게 효과를 줄 수 있다.

    var myTransition: AnyTransition {
        let insertion = AnyTransition.opacity
            .combined(with: .scale)
        
        let removal = AnyTransition.move(edge: .leading)
        return .asymmetric(insertion: insertion, removal: removal)
    }

 

이런식으로 custom한 AnyTransition을 만들어서 넣어줄 수 있다.

Text("안녕하세요")
	.transition(myTransition)

 

놀라운것은...

struct TransitionExampleView: View {
 
    var myTransition: AnyTransition {
        let insertion = AnyTransition.opacity
            .combined(with: .scale)
        
        let removal = AnyTransition.move(edge: .leading)
        return .asymmetric(insertion: insertion, removal: removal)
    }
    
    @State private var showText = false
    
    var body: some View {
        VStack {
            if showText {
                Text("안녕하세요")
                    .transition(myTransition)
            }
                
            Button {
                withAnimation {
                    self.showText.toggle() // 애니메이션과 함께 상태 토글
                }
            } label: {
                Text("버튼 보이기")
            }
        }
    }
}

 

프리뷰에 이 뷰 하나만 넣고 하면 나타날 때 transtion이 적용되지 않는데,

 

struct MyView: View {
    var body: some View {
        HStack {
            TransitionExampleView()
            TransitionExampleView()
        }
    }
}

 

이런식으로 여러개가 있으면 나온다.. 왜지?

 

뭘까..

아마 SwiftUI 버그겠지

 

modifier

- active와 identity 매개 변수에 ViewModifier 프로토콜을 채택하는 타입을 제공

- active: 뷰가 제거되기 직전의 상태

- identity: 뷰가 삽입되어 보이게 될 상태

애니메이션의 시작과 끝에서 이 두 매개 변수 값의 차이만큼 전환 효과가 발생하게 되며, 이것을 이용해 트렌지션을 만들 수 있다.

 

struct CustomScaleModifier: ViewModifier {
    let scale: CGFloat
    
    func body(content: Content) -> some View {
        content
            .scaleEffect(scale)
    }
}

 

이런식으로 Modifer를 하나만들고,

                Text("안녕하세요")
                    .transition(.modifier(active: CustomScaleModifier(scale: 0),
                                          identity: CustomScaleModifier(scale: 1)))

 

이런식으로 사용한다.

View가 사라지기 직전에는 scale이 0이고,

View가 삽입되었을 때는 scale이 1이다.