33: Animations, part 2

그루두·2024년 5월 19일
0

100 days of SwiftUI

목록 보기
42/108
post-thumbnail

100 days of swiftui: 33
https://www.hackingwithswift.com/100/swiftui/33

animation stack

animation도 modifier이기 때문에 순서가 중요하다. 어떤 순서로 적용했는지, 어떤 것들을 적용했는지에 따라 달리 표현할 수 있다. 아래 예시는 색과 움직임에 각각 animation을 설정했다.

            Button("Tap here") {
                animationValue.toggle()
            }
                .padding(50)
                .background(animationValue ? .green : .blue)
                // 1
                .animation(.easeInOut(duration: 1).delay(1), value: animationValue)
                .foregroundColor(.white)
                .clipShape(.circle)
                .rotation3DEffect(
                    .degrees(animationValue ? 360 : 0), axis: (x: 1.0, y: 1.0, z: 1.0))
                // 2
                .animation(.spring(duration: 1, bounce: 0.6), value: animationValue)

배경색이 변경되는 animation은 1번으로 delay: 1초로 인해 1초 뒤에 표현된다. 그와 별개로 rotate animation은 delay 없이 spring 형태로 표현된다. 이처럼 animation은 자신의 이전 animation modifier의 다음 코드부터 자신의 이전 코드까지 적용된다.

그래서 변경되는 값 이전에 animation modifier를 설정하면 먹히지 않는다.

예:

            Button("Tap here") {
                animationValue.toggle()
            }
                .padding(50)
                .animation(.spring(duration: 1, bounce: 0.6), value: animationValue)
                .animation(.easeInOut(duration: 1).delay(1), value: animationValue)
                .background(animationValue ? .green : .blue)
                .foregroundColor(.white)
                .clipShape(.circle)
                .rotation3DEffect(
                    .degrees(animationValue ? 360 : 0), axis: (x: 1.0, y: 1.0, z: 1.0))

animation with DragGesture

drag를 활용하여 animation을 만들 수도 있다. 그리고 그 중 어디에 animation을 적용하냐에 따라 달리 표현할 수 있다.

아래 예시 중 왼쪽은 drag를 통해 dragAmount의 변화에 animation을 적용시켰고, 오른쪽은 drag가 끝난 후(onEnded) 원위치로 돌리는 것에 animation을 적용시켰다.

        RadialGradient(colors: [.cyan, .green, .yellow], center: .center, startRadius: 180, endRadius: 0)
            .frame(width: 350, height: 350)
            .clipShape(.rect(cornerRadius: 20))
            .offset(dragAmount)
            .gesture(
                DragGesture()
                    .onChanged { dragAmount = $0.translation }
                    .onEnded { _ in
                    	// drag가 끝난 후(`onEnded`) 원위치로 돌리는 것에 animation
                        withAnimation(.smooth) {
                            dragAmount = CGSize.zero
                        }
                    }
            )
            // dragAmount`의 변화에 animation
            .animation(.smooth, value: dragAmount)

개인적으로 swiftui에서 drag를 어떻게 적용시킬 수 있을지 궁금했는데 알게 되어 기쁘다.

코드 파일
https://github.com/treesofgroo/Ios-Animations/commit/0d5b683a7099ae4b0807882724e5d70e914f81fd

아래는 조금 수정한 다른 예시다.

        HStack(spacing: 0) {
            ForEach(0..<letters.count, id: \.self) { num in
                Text(String(letters[num]))
                    .font(.title)
                    .padding(10)
                    .frame(width: 40, height: 40)
                    .background(enabled ? .yellow : .green)
                    .clipShape(.circle)
                    .offset(dragAmount)
                    .animation(.linear.delay(Double(num) / 20), value: dragAmount)
            }
        }
        .gesture(
            DragGesture()
                .onChanged { dragAmount = $0.translation }
                .onEnded { _ in
                    dragAmount = .zero
                    enabled.toggle()
                }
        )

코드 파일
https://github.com/treesofgroo/Ios-Animations/commit/9913dd9d07471731511056878d0a844feb0c0805

showing and hiding with transition

특정 View가 나타나고 사라지는 것을 animation으로 나타낼 수 있는데, 이때 transition을 통해 그 모양새를 설정할 수 있다. 그리고 asymmetric을 통해 그 애니메이션의 초반과 후반을 다른 모양새로 설정할 수 있다.

        VStack {
            if isShowingElement {
                Circle()
                    .frame(width: 200)
                    .transition(.asymmetric(insertion: .slide, removal: .push(from: .top)))
            }
                
            Button("Tap Here") {
                withAnimation {
                    isShowingElement.toggle()
                }
            }
            .padding()
            .buttonStyle(.borderedProminent)
        }

asymmetric(insertion:removal:)
https://developer.apple.com/documentation/swiftui/anytransition/asymmetric(insertion:removal:)

❔ 이유는 알 수 없지만 버튼을 반복해서 누르다 보면 설정한 transition animation과 달리 표현된다.

코드 파일
https://github.com/treesofgroo/Ios-Animations/commit/622ca43aca99af7a4c158f74c510cf24ec1773f9

custom transition

ViewModifier를 활용해서 transition 동작을 직접 만들 수도 있다. 아래 예시는 rotationEffect을 설정해서 z축 위치를 임으로 설정해서 요소를 회전시키면서 나타낸다.

  • ViewModifier로 회전 설정하여 pivot이라는 transition 만들기
struct CornerRotateModifier: ViewModifier {
    let amount: Double
    let anchor: UnitPoint

    func body(content: Content) -> some View {
        content
            .rotationEffect(.degrees(amount), anchor: anchor)
    }
}

extension AnyTransition {
    static var pivot: AnyTransition {
            .modifier(
                active: CornerRotateModifier(amount: -90, anchor: .topLeading),
                identity: CornerRotateModifier(amount: 0, anchor: .topLeading)
            )
        }
}
  • View에 transition animation 설정하기
struct ContentView: View {
    @State private var isShowingElement = false
    
    var body: some View {
        ZStack {
            Circle()
                .foregroundColor(.green)
                .frame(width: 200)
            if isShowingElement {
                Circle()
                    .foregroundColor(.orange)
                    .frame(width: 200)
                    .transition(.pivot)
            }
        }
        .onTapGesture {
            withAnimation {
                isShowingElement.toggle()
            }
        }
    }
}

참고로 clipped()를 추가해서 뒤의 Circle() 범위 내에서 표현하는 방법도 있다.

struct CornerRotateModifier: ViewModifier {
    let amount: Double
    let anchor: UnitPoint

    func body(content: Content) -> some View {
        content
            .rotationEffect(.degrees(amount), anchor: anchor)
            .clipped()
    }
}

코드 파일
https://github.com/treesofgroo/Ios-Animations/commit/802f5cc8aa0b934a8a2da2385a8f4cf7f9261bca

profile
계속 해보자

0개의 댓글

관련 채용 정보