Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

teunteun2

SwiftUI 나를 위한 wiki(2) - Animation & Transition 본문

SwiftUI

SwiftUI 나를 위한 wiki(2) - Animation & Transition

teunteun2 2023. 8. 2. 20:43

Animation

Structure

 

정의

특정 State에서 다른 State로 변경될 때 뷰의 변화를 부드러운 시각적 transition을 주기 위한 방법

 

OverView

An Animation provides a visual transition of a view when a state value changes from one value to another. The characteristics of this transition vary according to the animation type.

 

View 말고도 바인딩 값이 변경될 때 뷰에서 애니메이션을 수행할 수도 있다.

바인딩 값에 애니메이션 종류를 특정짓고 싶으면, 바인딩 값에 Animation의 인스턴스 메서드인 .animation(_:) 메서드를 호출하면 된다.

 

밑의 예제코드에서

1. 위 ContentView에서는 일반적으로 View에 animation modifier을 붙였고

2. 아래의 ContentView에서는 토글될 때 변경되는 bindingValue인 $isTrailing  .animation(_: ) 메서드를 붙여

애니메이션을 구현했다.

 

struct ContentView: View {
    @State private var scale = 0.5

    var body: some View {
        VStack {
            Circle()
                .scaleEffect(scale)
                .animation(.easeIn.repeatCount(3), value: scale)
            HStack {
                Button("+") { scale += 0.1 }
                Button("-") { scale -= 0.1 }
            }
        }
        .padding()
    }
}

// perfrom an animation when a binding value changes
struct ContentView: View {
    @State private var isTrailing = false

    var body: some View {
       VStack(alignment: isTrailing ? .trailing : .leading) {
            Image(systemName: "box.truck")
                .font(.system(size: 64))


            Toggle("Move to trailing edge",
                   isOn: $isTrailing.animation(.linear))
        }
    }
}

 

Transaction

Struct

 

정의

The context of the current state-processing update

현재 상태의 변화를 처리하기 위해 SwiftUI가 알아야하는 context를 가지고 있는 value 라고 한다.

  • 보간을 계산하기 위한 애니메이션 함수에 특히 중요하다.
  • SwiftUI에게 애니메이션 수행 혹은 다른 state에 의존적인 변화에 꼭 필요한 정보들을 제공한다.
  • SwiftUI 어플리케이션에서 state 변화가 생기면 무조건 생성된다.
  • state 변화가 명시적(이하 explicit) 애니메이션 (.animation)으로 일어나든 암묵적(이하 implicit) 애니메이션(.withAnimation)으로 일어나든 상관없이 생성되며, 필요한 뷰 계층구조에 변화를 전파한다.
  • SwiftUI의 environment 값과 비슷하게, 암묵적으로 뷰 계층구조를 타고 내려간다
  • Transaction의 생성과 전달는 explicit/implicit 애니메이션의 위치와 로직에 연결되어있다.
  • Transaction은 자기 스스로 생성되거나 전달될 수 없다.
  • Transaction은 SwiftUI의 .transaction modifier을 통해 관찰되거나 조종될 수 있다.
  • State 변화에 대한 애니메이션 설정을 유지하며 이러한 State 변화의 영향을 받는 View들로 전달된다.
    1. State 변화가 일어나면, Animatable protocol을 따르는 animatable components는 State 변화에 관한 맥락(transaction)을 받게되고
    2. transaction으로부터 애니메이션 곡선 함수를 얻으며
    3. 이를 통해 애니메이션에 필요한 보간을 계산한다

 

단지 state 업데이트에 관한 context라는 정의가 와닿지 않아서, 다른 기술블로그의 자세한 설명을 참고했다.

Animation과 Transaction 그리고 전반적인 SwiftIUI의 애니메이션에 대해 이해하는데에 큰 도움이 되었다.

https://holyswift.app/difference-between-implicit-and-explicit-animations-in-swiftui/

 

Difference Between Implicit and Explicit Animations in SwiftUI - Holy Swift

Discover the power of implicit and explicit animations in SwiftUI and gain full control over your app's transitions.

holyswift.app

 

OverView

Transaction을 사용하여 뷰 계층의 View 간에 애니메이션을 전달한다
상태 변경에 대한 root transaction은 변경된 binding으로부터 발생하며

withTransaction(_:_:) 또는 withAnimation(_:_:)을 호출하여 어떤 글로벌 값이든 설정할 수 있다.

 

 

 

 

 


 

 

Animation 문서 가장 밑 See Also 부분을 보면

Adding state-based animation to an action 이라는 란에 withAnimation(_: _:) 함수가 기재되어있다.

 

https://developer.apple.com/documentation/swiftui/animation

 

 

WithAnimation(_:_:)정의 : 제공된 애니메이션을 바탕으로 뷰의 body를 recomputing 한 Result을 리턴한다.

밑에 completion이 달려있는 함수는 애니메이션이 끝나고 다른 동작을 부가적으로 수행할 수 있는 것 같은데 아직 beta 인가봄 (23.08.02 작성)

 

그럼 위에서 바인딩 값 변경에 애니메이션을 주기 위해 사용했던 .animation(_: value:) 메서드와 .withAnimation(_: _:)

무슨 차이점이 있는걸까?

 

 .animation(_:)   vs   .withAnimation(_: _:)

  • .animation(_:) 은 binding value가 변경되었을 때 애니메이션을 명시하기 위한
    Instance Method 이다 (return Binding<Value>) -> Explicit Animation
  • .withAnimation(_: _:) 은 제공된 애니메이션을 바탕으로
    View body를 recomputing 한 Result 를 반환하는 Function 이다 -> Implicit Animation

 

"이 기능은 지정된 Animation을 스레드의 현재  Transaction의 animation property로 설정합니다."

 

무슨말인지 잘 모르겠다.

 

explicit / implicit animation이란 각각 무엇이며 .animation 과 .withAnimation의 차이는 무엇일까

 

Implicit Animation

SwiftUI의 Implicit animation은 .animation , .transaction modifier로 선언되며,

어떤 transaction이 state 변화가 발생했을 때 생성되어야 하는지 나타낸다.

 

앱 내부에서 State 변화가 일어나면

SwiftUI는 State변화가 일어난 View에서 modifier로 선언된 implicit animation을 바탕으로 새로운 transactions를 생성한다.

그런 다음 State변화가 일어난 View의 계층구조를 따라 그 하위 뷰까지 transactions이 되며

이 transactions은 state변화가 일어나는 동안 animatable components가 어떻게 보간되어야 하는지 알려주는 것이다.

 

Image, Rectangle, Text, Button이 차례대로 놓여져있는 View가 있고,

isExpanded에 따라 height이 변화하는 Rectangle에만 애니메이션을 준 코드이다.

import SwiftUI

struct ContentView: View {
    @State var isExpanded = false
        
        var body: some View {
            VStack {
                Image(systemName: "star.fill")
                    .imageScale(.large)
                    .foregroundColor(.pink)
                    .foregroundStyle(.tint)
                    
                
                Rectangle()
                    .frame(height: isExpanded ? 500 : 1)
                    .foregroundColor(.pink)
                    .animation(.default, value: isExpanded)
                
                Text("animation example")
                    .font(.title2)
                
                Button {
                    isExpanded.toggle()
                } label: {
                    Text("Change Size")
                }.buttonStyle(.bordered)

            }
            .padding()
        }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

버튼을 눌러보면 animation은 Rectangle에서만 부드럽게 실행되고, 위아래에 배치된 다른 View들은 애니메이션 없이 늘어난 Rectangle height에 의해 뚝뚝 끊기듯이 위치가 변경된다.

 

이를 보완하려면 Implicit animation의 특징인

애니메이션이 부여된 뷰 계층을 따라 하위 View에도 animation이 적용되는 것을 이용해 수정하면 된다.

애니메이션을 Vstack 에 부여하는 것이다.

 

import SwiftUI

struct ContentView: View {
    @State var isExpanded = false
        
        var body: some View {
            VStack {
                Image(systemName: "star.fill")
                    .imageScale(.large)
                    .foregroundColor(.pink)
                    .foregroundStyle(.tint)
                    
                
                Rectangle()
                    .frame(height: isExpanded ? 500 : 1)
                    .foregroundColor(.pink)
                
                Text("animation example")
                    .font(.title2)
                
                Button {
                    isExpanded.toggle()
                } label: {
                    Text("Change Size")
                }.buttonStyle(.bordered)

            }
            .animation(.default, value: isExpanded)
            .padding()
        }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

 

하지만 implicit animation은 어떤 State가 변화되었을 때 애니메이션이 변경되는지 추적이 어려울 수 있다.

이럴 때 사용하는 것이 Explicit Animation이라고 한다.

 

 Explicit Animation

SwiftUI에서의 Explicit Animation은 withAnimation , withTransaction 글로벌 함수를 이용해서

명령형 프로그래밍을 통해 생성된다.

 

explicit animation은 어디에서 withAnimation 함수가 실행되든 SwiftUI는 explicit animtation에 의해 생성된 transaction을

rootView에서부터 전파할 것이다.

 

개발자에 의해 명시적으로 생성되고 조종 가능하다는 것이 Implicit animation과 다른점이다.

 

import SwiftUI

struct ContentView: View {
    @State var isExpanded = false
        
        var body: some View {
            VStack {
                Image(systemName: "star.fill")
                    .imageScale(.large)
                    .foregroundColor(.pink)
                    .foregroundStyle(.tint)
                    
                
                Rectangle()
                    .frame(height: isExpanded ? 500 : 1)
                    .foregroundColor(.pink)
                
                Text("animation example")
                    .font(.title2)
                
                Button {
                    withAnimation {
                        isExpanded.toggle()
                    }
                } label: {
                    Text("Change Size")
                }.buttonStyle(.bordered)

            }
            .padding()
        }
}

 

버튼의 토글에 withAnimation 으로 애니메이션을 부여하면, 모든 뷰의 계층구조가 똑같은 보간 애니메이션 함수를 가지는 똑같은 transaction이 발생하는 것을 볼 수 있다.

 

Transaction은 State 변화가 일어났을 때 무조건 생성되어 뷰 계층구조에 모두 영향을 끼치므로

컴포넌트 분리가 중요할 것이다.

'SwiftUI' 카테고리의 다른 글

SwiftUI 나를 위한 wiki(1) - App, Scene, View  (0) 2023.08.01