-
Ch14. AnimationsRay Wenderlich/SwiftUI 2021. 2. 14. 18:23
SwiftUI의 애니메이션은 UIKit보다 훨씬 간단하다.
Animating state changes
직역해보면 <State변화를 애니메이션 한다>
물음표 짓게 만들지만 코드를 보면 이해가 된다.
Adding animation
Image(systemName: "chevron.up.square") .rotationEffect(.degrees(showDetails ? 0 : 180)) .animation(.default)
1, 2번 라인은 showDetails값에 따라 rotation을 업데이트한다. 즉 3번 라인이 없어도 무방하다.
.animation(.default) 한 줄로 이미지가 회전하는 애니메이션이 생겼다.
0과 180을 오가는 애니메이션은 정방향 기준으로 시계방향으로 회전할 것이다.
하지만 180대신 -180을 넣으면 반시계방향으로 회전한다.
Animation types
Default animation
.animation(.default)
가장 기본적인 Linear한 애니메이션
Eased animations
.animation(.easeOut) .animation(.easeIn) .animation(.easeInOut) .animation(.easeOut(duration: 2)) // duration설정 당연히 가능 .timingCurve(_:_:_:_) // 베지어 곡선을 사용한 커스텀 커브 사용 가능
Spring animations
Spring 애니메이션에는 4가지 요소가 있다.
- Mass: 스프링에 매달린 추의 무게라고 생각해보자. 무거울수록 더 큰 힘이 작동해서 스프링을 많이 늘릴 것이다.
- Spring Resistance: 스프링이 얼마나 쫀쫀한가? 값이 클수록 덜 늘어난다.
- Damping: 스프링을 제어하는 외부힘의 요소. 외부 힘이 강할수록 스프링의 튕김이 빨리 끝날 것이다.
- Initial Velocity: 스프링에 물체를 매달고 그 물체를 던지는 힘이라고 생각하자.
Creating spring animations
animation(.interpolatingSpring(mass: 1, stiffness: 100, damping: 10, initialVelocity: 0))
좀 더 쉽게 사용가능한 스프링도 제공한다.
.animation(.spring(response: 0.55, dampingFraction: 0.45, blendDuration: 0))
- dampingFraction: 얼마나 빨리 스프링을 멈출 것인지? 0을 입력하면 멈추지 않는다.
- response: dampingFraction이 0일 때 1번 스프링하는데 걸리는 시간
- blendDuration: control for blending the length of the transition between different animations
Removing and combining animations
Button(action: { self.showDetails.toggle() }) { HStack { Text(showDetails ? "Hide Details" : "Show Details") Spacer() Image(systemName: "chevron.up.square") .scaleEffect(showDetails ? 2 : 1) // 1 .animation(nil) // 2 .animation(.spring(response: 0.55, dampingFraction: 0.45, blendDuration: 0)) .rotationEffect(.degrees(showDetails ? 0 : 180)) .animation(.easeInOut) } }
scaleEffect와 rotationEffect 2개의 state변화가 있다. animation은 하나만 전달되었다.
실행하면 2개의 effect가 동시에 애니메이션 된다.
- scale은 애니메이션 하기 싫다면 1번 주석을 해제하면 된다.
- scale과 rotation에 다른 애니메이션을 사용하고 싶다면 2번 주석을 해제하면 된다.
Animating from state changes
위에서는 state변화가 생긴 View에 애니메이션을 추가했다. 하지만 state변화를 발생시키는 곳에서 애니메이션을 추가할 수 있다.
Button(action: { withAnimation(.default) { self.showDetails.toggle() } }) { HStack { Text(showDetails ? "Hide Details" : "Show Details") Spacer() Image(systemName: "chevron.up.square") .scaleEffect(showDetails ? 2 : 1) .rotationEffect(.degrees(showDetails ? 0 : 180)) } } FlightDetails(flight: flight) .offset(x: showDetails ? 0 : -UIScreen.main.bounds.width)
withAnimation함수를 사용하여 상태변화가 발생하는 곳에 애니메이션을 추가했다.
실행하면 showDetails에 관련된 모든 뷰가 애니메이션을 하며 변경된다.
여기서도 마찬가지로 scaleEffect만 스프링을 적용하고싶을 경우, scaleEffect에만 스프링 애니메이션을 추가하면 된다.
Adjusting animations
Delay
FlightDetails(flight: flight) .offset(x: showDetails ? 0 : -UIScreen.main.bounds.width) .animation(Animation.spring().delay(1))
Speed
.animation(Animation.spring().speed(2))
Repeating animations
.animation(Animation.spring().repeatCount(2, autoreverses: false))
Animating view transitions
View를 보여주거나 숨길 때 사용한다. 트랜지션도 애니메이션이다. 따라서, 트랜지션에 영향을 주는 State변화를 withAnimation으로 감싸주어야 한다.
Button(action: { withAnimation { self.showDetails.toggle() } }) {
if showDetails { FlightDetails(flight: flight) .transition(.slide) }
애니메이션과 트랜지션의 차이점 중 하나는 애니메이션으로 뷰를 숨길 때는, 보이지 않아도 뷰가 존재한다는 것이다. 하지만 트랜지션을 사용하면 뷰를 보여줄 때 생성하기 때문에 리소스 관리에 유리하다.
View transition types
위 코드에서는 slide라는 타입을 사용했다. 왼쪽에서 뷰가 나타나고 오른쪽으로 사라지는 애니메이션이다.
다른 타입은 아래 코드를 참고하자.
if showDetails { FlightDetails(flight: flight) // .transition(.slide) // .transition(.opacity) // .transition(.move(edge: .bottom)) // .transition(.scale(scale: 0.1, anchor: .topLeading)) .transition(.offset(x: 100, y: 100)) }
Extracting transitions from the view
커스텀한 트랜지션을 사용할 때, 뷰에 길게 나열하는 것보다는 커스텀 트랜지션을 선언하는게 좋다.
extension AnyTransition { static var flightDetailsTransition: AnyTransition { // implement custom transitino here AnyTransition.slide } }
if showDetails { FlightDetails(flight: flight) .transition(.flightDetailsTransition) }
Async transitions
뷰를 Add할 때, Hide할 때 다른 트랜지션을 사용할 수 있다. 동시에 여러 트랜지션을 사용하는 것도 가능하다.
extension AnyTransition { static var flightDetailsTransition: AnyTransition { let insertion = AnyTransition.move(edge: .trailing) .combined(with: .opacity) let removal = AnyTransition.scale(scale: 0.0) .combined(with: .opacity) return .asymmetric(insertion: insertion, removal: removal) } }
'Ray Wenderlich > SwiftUI' 카테고리의 다른 글
Ch13. Drawing & Custom Graphics (0) 2021.02.14 Ch12. Conditional Views (0) 2021.02.14 Ch11. Lists & Navigation (0) 2021.02.07 Ch9. State & Data Flow (0) 2021.02.07 Ch8. Introducint Stacks & Containers (0) 2021.02.07