일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- launch screen
- 스켈레톤 UI
- 파노라마 뷰
- 스켈레톤 통합
- 앱 성능 개선
- Android
- React-Native
- Skeleton UI
- boilerplate 제거
- panorama view
- Native Module
- 리액트 네이티브
- privacyinfo.plist
- React Native
- react-native-fast-image
- 스플래시스크린
- 리액트
- 3b52.1
- ios
- launchscreen
- react
- native
- Privacy manifest
- 네이티브
- 360도 이미지 뷰어
- requirenativecomponent
- 360도 뷰어
- 리엑트 네이티브
- 라이브러리 없이
- 360도 이미지
- Today
- Total
Neoself의 기술 블로그
SwiftUI 애니메이션 정리(Animatable, Animation, Transaction) 본문
이글은 Apple Developer 유튜브 채널의 "SwiftUI 애니메이션 알아보기" 영상의 내용을 정리한 내용을 담고 있습니다.
https://www.youtube.com/watch?v=IuSuHJs5-KE&list=LL&index=12
가장 먼저 Animation의 근간이 되는 뷰 업데이트에 대해 톺아보겠습니다.
SwiftUI View는 뷰의 종속항목을 상시 추적합니다. 여기서 종속항목은 @State, @ObservedObject, @Published 등의 프로퍼티 래퍼가 될 수 있는데요.
종속항목이 변경되면서 뷰의 상태가 변경되면, 아래의 절차가 연차적으로 수행됩니다.
1. 다운스트림 속성값이 만료되며 기존 View 무효화
2. Transaction 생성
3. 업스트림 종속성이 변경되며 프레임워크가 View Value을 호출
3. Transaction으로 뷰 계층 구조를 순회
4. 속성값 모두 업데이트된 후, Transaction 폐기
*여기서 Transaction은 딕셔너리 중 하나로 SwiftUI가 현재 업데이트에 대한 컨텍스트를 암시적으로 전파하는데 사용되는 강력한 데이터 흐름 구조를 의미합니다.
아래는 Apple Developer 유튜브 채널의 "SwiftUI 애니메이션 살펴보기" 영상에 소개된 다이어그램입니다.
탭 상호작용을 통해 업스트림 Dependency인 selected 상태변수가 변경될 시, 프레임워크는 좌측 다이어그램의 새로운 뷰에 표시될 정보들을 담은 View Value를 호출합니다. 이후 열리는 Transaction은 View Value를 활용해 UI 새부항목과 매핑되는 초록색의 속성들을 하나하나씩 순회하며 새로고침을 진행해줍니다. 순회가 완료되면서 뷰 업데이트가 완료되면 Transaction은 종료되고, View Value은 폐기됩니다. 마지막으로 랜더링 업데이트를 위해 Attribute가 드로잉 커맨드를 랜더러에게 보냅니다.
Animatable
struct Avatar: View {
var pet: Pet
@State private var selected: Bool = false
var body: some View {
Image(pet.type)
.scaleEffect(selected ? 1.5 : 1.0)
.onTapGesture {
withAnimation {
selected.toggle()
}
}
}
}
위처럼 애니메이션 적용 방식중 하나인 withAnimation을 통해 트리거 함수를 래핑하게 될경우에는 속성그래프가 어떻게 변경하게 될까요?? onTapGesture 내부 클로저 실행 시, 가장 먼저 트랜잭션 애니메이션이 설정되고 그 이후에 selected가 토글되면서 뷰 상태 업데이트 과정이 진행되게 됩니다. 여기서 주목해야할 속성은 scaleEffect입니다. 이는 애니메이션이 가능한 특수 속성으로, Transaction이 해당 속성을 순회올때마다 Transaction으로부터 애니메이션이 설정되어있는지 확인하는 과정이 추가되는데요. 위 상황의 경우 Transaction은 애니메이션 정보를 담게 되기 때문에, 즉시 사본을 생성하고 사본값에서 Presentaion값을 구동해 새로운 값으로 값을 보간하는 과정을 진행하게 됩니다.
scaleEffect와 같은 속성은 내장되어있는 애니메이션 가능(Animatable) 속성으로, 뷰 코드 호출 없이 메인 스레드에서 작업 수행이 가능하기에 적은 리소스로 애니메이션을 동작할 수 있습니다. 하지만 Animatable API를 통해 직접 커스텀 에니메이션 정의하게 될 경우, 애니메이션의 모든 프레임에 대해 본문이 호출되면서, 레이아웃이 재실행되기 때문에 애니메이션 비용이 큰 폭으로 늘어나게 됩니다.
Animation
애플에서는 애니메이션을 "애니메이션 적용 가능한 데이터를 시간에 따라 보간하는 범용 알고리즘"으로 정의하고 있습니다.
애니메이션의 경우 아래 선택지들이 있습니다.
Spring(withAnimation의 기본 적용값)
- smooth, snappy, bouncy
Time curve
- linear, ease in, ease out, ease in and out
Higher Order(기본 애니메이션에 delay, repeat 부여)
Custom Animation(애니메이션 목표값이 도중에 변경될 경우를 대응하기 위한 shouldMerge, velocity 속성이 존재함)
Transaction
위에 소개한 withAnimation 외에도, transaction 수정자를 통해 애니메이션 Transaction을 접근해 애니메이션을 설정할 수도 있는데요.이는 이미지 뷰의 모든 파생 애니메이션에 영향을 줄 수 있기 때문에, 애니메이션 적용 범위를 특정 상태변수로만 특정할 수 있는 animation 뷰 수정자를 사용하는 것을 애플에서 권유하고 있습니다.
Image("exampleImage")
.scaleEffect(selected ? 1.5 1.0)
// transaction 수정자 내부에서 transaction 애니메이션 직접 설정해 본문 호출 시 속성이 애니메이션을 재정의하게 할 수 있음.
// 하지만, 뷰가 업데이트될때마다 모든 파생 애니메이션이 재정의되기 때문에, 돌발 애니메이션 발생 가능성 있음.
//.transaction {
// $0.animation = .bouncy
//}
// 특정 상태값에 대한 애니메이션만 지정하는 뷰 수정자로 돌발 애니메이션 발생 방지
.animation(.bouncy,value:selected)
.onTapGesture {
// transaction 애니메이션을 animation 뷰 수정자로 직접 설정하였기 때문에 불필요
//withAnimation(.bouncy) {
selected.toggle()
//}
}
또한 Transaction은 각 속성들을 위에서 아래로 하나씩 순회하면서 업데이트를 진행하기 때문에 아래와 같이 애니메이션이 뷰에 적용되는 시기와 방법을 세밀하게 조절할 수 있습니다.
Image("exampleImage")
.shadow(radius: selected ? 12 : 8)
.animation(.smooth,value: selected)
.scaleEffect(selected ? 1.5 1.0)
.animation(.bouncy,value: selected)
.onTapGesture {
withAnimation(.bouncy) {
selected.toggle()
}
}
위 코드의 경우, 같은 상태변수에 의존함에도 불구하고, shadow와 scaleEffect Animatable 속성에 각기 다른 애니메이션을 적용시킬 수 있게 됩니다.
다음에는 컴포넌트를 재사용하는 환경에서 발생할 수 있는 돌발 애니메이션 방지를 위한 Transaction 사용법에 대해 다루겠습니다.
감사합니다.
'개발지식 정리 > Swift' 카테고리의 다른 글
WCSession 사용기 - 아키텍처 구성 (2) | 2024.10.17 |
---|---|
PhaseAnimator, Keyframes으로 고급 애니메이션 구현하기 (6) | 2024.10.16 |
애플워치로 측정한 심박수 iOS 앱으로 전송하기 (6) | 2024.10.10 |
SwiftUI에서 애플, 구글 로그인 구현하기 (with Node.js) (1) | 2024.10.09 |
React Native와 비교해보는 Swift,SwiftUI (5) | 2024.10.08 |