Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[정주] WWDC23 ‐ Explore SwiftUI Animation 정리

유정주 JeongJu Yu edited this page Aug 4, 2024 · 1 revision

SwiftUI 애니메이션 살펴보기

  • SwiftUI를 개발하게 된 핵심 동기는 앱에 애니메이션을 쉽게 추가할 수 있도록 하는 것
  • Anatomy of an update (뷰의 렌더링을 업데이트)
  • Animatable로 애니메이션 적용 대상을 결정
  • Animation으로 시간에 따른 값을 보간, Transaction으로 현재 업데이트의 Context 전파 방법 살펴보기

View Rendering 업데이트

  • SwiftUI는 화면에 보이는 종속 상태를 추적함
  • 종속 항목이 하나라도 변경되면 View가 무효화
  • 각각의 노드를 Attribute라고 말함. Attribute는 UI의 세부 항목에 매핑됨
스크린샷 2024年08月04日 13 16 55
  • 트랜잭션이 종료될 때 프레임워크는 body를 호출해서 새 값을 생성 후 렌더링을 새로 고침함
    • Tap이벤트 발생 → 새로운 트랜잭션 생성 → @State 값 변경 → View 무효화 → body 호출 → 값이 업데이트 → 트랜잭션 닫힘
    • 스크린샷 2024年08月04日 13 24 49

애니메이션

  • withAnimation {}을 사용하면 트랜잭션 애니메이션이 설정됨
스크린샷 2024年08月04日 13 24 03
  • 애니메이션이 가능한 속성(e.g. scaleEffect)의 값이 변경되면 트랜잭션에 애니메이션이 설정되어 있는지 확인함
    • 설정되어 있다면 copy하고 애니메이션을 사용해 시간에 따라 이전 값에서 새 값으로 변화함
  • 애니메이션이 가능한 속성은 modal과 presentation 값을 모두 가지고 있음
  • SwiftUI는 애니메이션이 속성 그래프에 포함되는 시점을 파악 후 적절한 애니메이션이 가능한 속성을 호출, 다음 프레임 생성
    • scaleEffect처럼 자동으로 애니메이션이 가능한 속성이라면 SwiftUI가 효율적임
    • 스크린샷 2024年08月04日 13 27 46

Animatable과 Animation

  • Animatable은 애니메이션을 적용할 데이터를 결정
  • Animation은 시간에 따른 데이터의 변화를 결정함

Animatable

스크린샷 2024年08月04日 13 39 58
  • Animatable을 채택하면 animatableData 속성을 준수해야 함

    • animatableData: 애니메이션이 가능한 속성
    public protocol Animatable {
    	associatedtype AnimatableData: VectorArithmetic
    	var animatableData: AnimatableData { get set }
    }
  • Animatable은 View에 맞춰 애니메이션을 커스텀하고 싶을 때 사용함

    • RadialLayout은 기본적으로 최단 거리로 애니메이션됨
      • 세 개의 Avatar가 원 중앙을 거쳐서 최단 거리 직선으로 이동
    • Podium의 Angle값을 이용해서 정의해서 호를 따라 이동하도록 구현할 수 있음
    • 스크린샷 2024年08月04日 13 46 25
    • 딱 한 번만 계산하던 흐름에서 SwiftUI가 매번 각도를 계산해서 레이아웃을 다시 실행하도록 하기 때문
    • 커스텀한 애니메이션은 매 애니메이션 프레임마다 body를 실행하기 때문에 애니메이션 비용이 훨씬 많이 든다.
    • 따라서 내장된 애니메이션으로 원하는 결과를 얻을 수 없을 때만 사용해야 함 → 가벼운 마음으로 사용하면 안 됨

Animation

  • SwiftUI에는 여러 강력한 애니메이션이 내장되어 있음
    • Timing curve
    • Spring
    • Higher order (기본 애니메이션을 수정한 고차원 애니메이션)
  • Timing curve는 애니메이션 속도를 정의하는 curve와 duration을 가짐
    • curve는 Bezier 곡성의 제어점을 사용해 만듬
    • duration을 커스텀할 수 있음
  • Spring 애니메이션은 스프링 시뮬레이션을 실행해 주어진 시점의 값을 결정함
    • mass, stiffeness, damping을 조절하는 방식이었는데, 이걸 조절하는건 직관적이지 않음
    • 그래서 bounce를 조절하는 방식으로 변경함
    • duration과 extraBounce(탄력도)를 조절할 수 있음
    • Spring 애니메이션은 UI에 유기적인 느낌을 주기 때문에 강추
      • iOS 17에서는 withAnimation의 기본값이 Spring임
  • Higher order 애니메이션은 기본 애니메이션을 커스텀할 수 있음
    • 반복, 속도변경, 역재생 등

Custom Animation

  • Custom Animation 프로토콜이 추가됨

  • animate, shouldMerge, velocity 메서드를 구현해야 함

    • animate는 필수, 나머지는 선택
    public protocol CustomAnimation: Hashable {
    	func animate<V: VectorArithmetic>(
    		value: V, time: TimeInterval, context: inout AnimationContext<V>
    	) -> V?
    }
  • shouldMerge는 애니메이션 도중에 새로운 애니메이션이 생성됐을 때 애니메이션 상태를 통합할 수 있음

  • velocity는 애니메이션이 통합될 때 속도를 유지할 수 있음

Transaction

  • Dictionary를 사용하여 애니메이션 상태를 기록한다.

  • animation 모디파이어를 사용해 특정 값이 바뀌었을 때 애니메이션을 트랜잭션에 기록할 수 있다.

    • transaction 을 사용해서 여러 애니메이션을 정의하면 예상치 못한 돌발 애니메이션이 발생할 수 있음
    • 이를 방지하기 위해 animation 모디파이어가 생김
  • animation 모디파이어의 순서를 조정하면 효과에 따라 적용할 애니메이션을 다르게 기록할 수 있음

    struct Avatar: View {
     var pet: Pet
     @Binding var selected: Bool
     var body: some View {
     Image(pet.type)
     .shadow(radius: selected ? 12 : 8)
     .animation(.smooth, value: selected)
     .scaleEffect(selected ? 1.5 : 1.0)
     .animation(.bouncy, value: selected)
     .onTapGesture {
     selected.toggle()
     }
     }
    }
    • shadow에는 smooth, scaleEffect에는 bouncy가 적용됨
      • 아래에서 위로 적용되는 게 포인트
      • bouncy로 기록 → scaleEffect에 적용 → smooth로 기록 → shadow에 적용
    • Dictionary로 관리되기 때문에 가능한 원리
  • Leaf 컴포너트가 아니면 돌발 애니메이션이 발생할 확률이 훨씬 높아짐

    • 이전에 적용된 트랜잭션 기록을 이어 받을 수 있기 때문
    • 이 상황을 방지하기 위해 Scoped Animation 모디파이어가 생김
    struct Avatar<Content: View>: View {
     var content: Content
     @Binding var selected: Bool
     var body: some View {
     content
     .animation(.smooth) {
     0ドル.shadow(radius: selected ? 12 : 8)
     }
     .animation(.bouncy) {
     0ドル.scaleEffect(selected ? 1.5 : 1.0)
     }
     .onTapGesture {
     selected.toggle()
     }
     }
    }
    • 특정 범위에만 애니메이션을 적용함
      • 애니메이션 트랜잭션을 복사한 뒤 사본에만 애니메이션을 기록하여 적용하는 원리
      • 스크린샷 2024年08月04日 14 15 52
      • 애니메이션 작업이 완료되면 사본은 제거됨
      • 원본은 그대로 애니메이션이 nil이므로 하위 View에는 애니메이션이 적용되지 않음
  • 트랜잭션 Key를 적용하여 트랜잭션 Dictionary 값을 설정할 수 있음

  • 모든 트랜잭션 값을 구조체 생명주기에서 고유함. 트랜잭션은 View 업데이트가 끝날 때마다 버려지기 때문

    • 매 업데이트마다 명시적으로 트랜잭션을 정의해야 함
    • 그렇지 않으면 기본값으로 설정됨
  • Transaction 모디파이어도 돌발 애니메이션을 방지하기 위해 값을 설정하거나 body로 범위를 지정할 수 있음

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /