본문 바로가기
iOS/SwiftUI

#14 Animations

by HaningYa 2020. 10. 27.
728x90

Animation의 필요성

  • good app 과 great app의 차이는 detail 에서 온다.
  • 적재적소에 Animation을 사용하는 건 사용자에게 즐거움과 다른 앱과의 차별성을 줄 수 있다.
  • Animation 은 앱 사용을 더 즐겁게 만들고 특정 영역에 사용자를 집중 시킬 수 있다.
  • 좋은 Animation은 앱을 매력적이고 사용하기 쉽게 만들어 준다.

SwiftUI 에서의 Animation

  • AppKit 이나 UIKit 보다 훨씬 간단하다.
  • SwiftUI Animation은 지루한 작업을 모두 처리하는 더 높은 수준의 추상화 이다.
  • animation들을 합치거나 겹치거나 고려할 사항없이 그냥 멈출 수 있다.
  • state 관리의 복잡성은 framework 가 담당한다.
  • 복잡성과 edge case 를 처리하는 대신 더 좋은 animation을 만들 수 있게 해준다.

Animating state changes

starter 프로젝트는 이전에 만든 flight 정보를 보여주는 앱이다.

Adding animation

버튼을 누르면 비행정보를 보여주고 숨기는 코드이다. 지금은 누를때 마다 animation 없이 바뀐다.

위 화살표 아이콘을 하나의 아이콘에 rotation을 돌려서 보여줄 것 이다. (현재는 위, 아래 아이콘 각각 사용중)

chevron 아이콘을 up 만 써서 선택됬을 경우 180도 돌려준다.

rotationEffect로 showDetail 상태변수에 따라 회전을 주었다.
animation 은 시작 상태에서 끝상태까지 일정 시간동안 변화한다.

이미지에 animation modifier를 달면

.animation(.default)

rotation 하는 animation을 볼 수 있다.

State가 바뀌면 0에서 180도로 회전하고 SwiftUI에게 .animation() modifier를 통해 animation을 동작시켰다.

modifier가 적용된 Image에만 오직 animation이 적용되었다.

animation을 만들때 중요한건 변화하는 각도이다.
반만 회전하는 최종 각을 -180도로 해도 상관없다. 그러나 돌아가는 방향이 반대인 걸 알 수 있다.

  • 양수각도는 시계방향
  • 음수각도는 반시계방향으로 돈다.

각도는 0에서 360에 제한되지 않는다. (-180 ~ 540도 가능하다.)

Animation types

default animation을 써봤다.

SwiftUI 는 다른 animation type을 제공한다.

작은 화살표에서 그 차이를 볼 수 없기 때문에 다른 view 에 animation을 넣어본다.

Animating Details view

showDetail이 true일 경우 FlightDetails를 보여주고 아닐 경우 안보여주는 코드이다.

view 를 adding 하고 removing 할 때 animation을 추가해본다.

먼저 view를 없애는게 아닌 화면 밖으로 밀어내서 안보이도록 코드를 수정한다.

 

스크린 너비만큼 FlightDetails view를 - 방향 (왼쪽) 으로 밀어낸다.

Default animation

offset 다음 default animation 을 추가한다.

default animation은 가장 간단한 animation 타입이다.
일정한 비율로 시작점에서 끝점으로 이동하는 animation을 보여준다.

Ease animations

ease는 가장 자주 쓰이는 animation이다.

acceleration과 deceleration을 시작점과 끝점에 적용한다.

ease animation은 실제 세계의 동작과 비슷하게 움직여서 자연스러운 효과를 제공한다.

.easein

easeout 은 default 값으로 0.35초의 재생시간을 가진다.
파라미터를 통해 시간을 정해줄 수 있다.

easeout 말고도 easeIn이 있다. (천천히 시작해서 가속붙음)

.easeout

두가지 타입을 합친 easeInOut 타입도 있다. 시작땐 가속과 끝날땐 감속을 한다. 

이 밖에 custom 하게 가속과 감속을 주고싶다면 timingCurve(_:_:_:_) 타입 메서드를 사용하면 된다.

SwiftUI 는 bézier 곡선을 활용한다,

Spring animations

eased animation은 시작점과 끝사이 한 방향으로 흘러가는 움직임이다.

절대 시작점과 끝점을 넘어가지 않는데 spring 이라는 animation으로 bounce 효과를 줄 수 있다.

Why a spring makes a useful animation

spring 은 늘어나는 것과 압축되는 것에 저항이 있다.

저 크게 줄일려고 할 수록 더 큰 저항을 낸다.

Spring의 물리적 특성을 마찰이 없는 가상 상황과 마찰이 있는 현실에서 설명하는 내용

마찰이 없는 곳에서 spring 저항
마찰 있는 곳에서 spring 저항

이러한 그래프를 그리는 덴 4가지 요소가 있다.

  • Mass (질량): 무게이다. 무게가 클 수 록 더 bounce 한다.
  • Spring Resistance: 스프링이 빡셀수록 더 큰 저항을 만든다.
  • Damping: system 내에서 다른 저항 요소들 (damping이 많으면 더 빨리 저항이 줄어듬)
  • initial velocity : 초기 속도

그래서 Spring animation 배우는데 어쩌라고라 할 수 있는데 

SwiftUI에서는 이 damped simple harmonic motion 을 실제 세상과 동일하게 만든다.

방금 말한 4가지 요소와 같이 spring animation을 만들때도 4가지 파라미터가 필요하다.

Creating spring animations

이제 spring animation 이 어떻게 동작하는지 알았으면 만들어 본다.

  • mass : bounce 를 얼마나 할지
  • stiffness : 처음 움직임의 속도
  • damping: 얼마나 느려지거나 빨라질지
  • initialVelocity : 추가적인 초기 모션을 준다.

mass 가 크면 오랫 동안 튕기고 한번 튕길때 마다 멀리 이동한다.
mass 가 작으면 더 빨리 멈추고 각각의 지점까지 적게 이동한다.

stiffness를 증가하면 끝지점을 넘어가게 만드는데 animation 지속 길이에는 영향을 주지 않는다.

damping을 증가하면 animation이 부드럽고 빨리 끝나게 된다

initial velocity를 증가시키면 더 멀리 bounce 하게 한다.

inital velocity에 음수를 취하면 렉이 발생하는데 움직임이 반대방향의 초기 속도를 이겨내야 하기 때문이다.

SwiftUI는 좀더 직관적으로 Spring animation을 할 수 있는 방법을 제공한다.

dampingFraction 은 spring 다움이 얼마나 빨리 멈추는지 제어한다.

값이 0 이라면 spring animation은 멈추지 않는다. ( = undamped spring)

값이 1이상이라면 진동없이 멈춘다 (overdamped , eased)

보통은 0과 1사이값을 정한다.

response 파라미터는 dampingFraction이 0일 때 하나의 진동을 완료하는데 걸리는 시간을 뜻한다.

animation이 끝나기 까지 시간을 정한다.

blendDuration 파라미터는 다른 animation 사이 transition의 길이를 정한다.

값이 0 일 경우 blending 을 끈다.

Removing and combining animations

animation 에게 nil을 넣는다.

화살표를 2배로 커지게 하는 코드이다.

실행해보면 커지는 동작도 rotation이 같이 움직이는 걸 볼 수 있다.

animation은 animation modifier를 적용하는 요소에서 발생하는 모든 상태 변경에 영향을준다.

scaleEffect () 및 rotationEffect () 메서드 사이에 .animation (nil)을 추가하면 화살표가 커진다음 rotating animation이 적용되는 걸 볼 수 있다.

scale 에 대해서 spring이 적용되고 rotation에 대해서 easeInOut 이 적용된다.

Animating from state changes

이번 챕터까지 변경된 뷰 요소에 애니메이션을 적용했다.

상태 변경이 발생하는 지점에 애니메이션을 적용 할 수도 있는데 State 변경으로 인해 발생하는 모든 변경 사항에 애니메이션이 적용할 수 있다.

Image 와 FlightDetails 에 animation modifier를 없애고 Button action 내에

showDetails라는 State 를 감싸는 withAnimation()을 추가했다.

확인해 보면 showDetails State 에 영향을 받는 모든 요소에 animation이 적용된 걸 볼 수 있다.

이렇게 여러곳에 필요한 animation을 편한게 한번만에 적용할 수 있다.

만약에 animatio을 직접 또 준다면 state 에서 준 animation을override 한다. 

Adjusting animations

animation에 따라 공통적인 instance methods들이있다.

animation을 delay 하거나 속도를 바꾸고 반복하는 등의 효과를 줄 수 있다.

Delay

delay() 메서드는 animation이 일어나기 전 시간을 정할 수 있게 해준다.

*.spring() 은 되는데 여기에 .delay를 붙이려면 Animation이라는 context를 명시해야된다.

 

Allow chained member references in implicit member expressions

Introduction Following up on the discussion thread from the other day, I propose that we extend implicit member syntax (a.k.a. "leading dot syntax") to allow for chained member references rather than just a single level. Motivation When the type of an expr

forums.swift.org

Speed

.speed()를 통해 animation의 속도를 정할 수 있다.

주어진 값으로 animation 배속을 결정한다.

만약에 2초간 재생되는 animation에 .speed(0.5) 를 적용하면 4초동안 animation을 재생한다.

이 modifier는 .default나 spring animation과 같이 직접적인 재생 시간을 정할 수 없는 animation에 유용하게 쓰인다.

Repeating animations

animation을 반복하려면 .repeatCount(_:autoreverse:)에 반복횟수를 넣으면 된다.

animation reverse도 정할 수 있다.

reverse 없이는 animation는 동작이 끝난 뒤 초기점으로 돌아가 다시 animation을 동작하고

reverse true 면 반복하기 전에 초기점으로 돌아간다.

repeatingForever(autoreverse:)는 animation 을 영원히 돌린다.

Extracting animations from the view

view 밖에 animation을 정의해서 재사용성을 높여본다.

FlightBoardInformation 에 body 위에 코드 입력

이것은 custom animation property 이다. 

button 에서 withAnimation 부분을 수정한다.

이렇게 view code 에 animation을 넣지말고 따로 빼주어 복잡한 animation의 가독성을 높여줄 수 있다.

Animating view transitions

*transitions는 preview 에서 가끔 잘못 렌더링 된다. 잘안되면 시뮬레이터에서 돌려라

Transition은 view 를 보여주고 숨기는 animation 이다.

기본적으로 view는 fading in, out 을 통해 화면을 끄고 킨다.

animation은 대부분 transition과 같이 동작하는 걸 볼 수 있다.

transition은 animation type 이기 때문에 state 주위에 withAnimation() 를 지정해야 한다. 그렇지 않으면 SwiftUI는 transition을 표시하지 않는다.

위와 같이 작성하면 view 가 왼쪽에서 slide 들어오고 오른쪽으로 나가는 걸 볼 수 있다.

원래라면 offset 를 바꿔서 효과를 줬지만 slide transition 을 사용해서 위치에 관한 건 SwiftUI 가 알아서 해줬다.

이전의 방식은 detail view 가 항상 있는 대신 화면 밖으로 보냈었다. 그래서 view 가 보이지 않아도 리소스가 필요했는데
이제는 view 가 추가될 때 SwiftUI 가 animation을 넣어준다.

SwiftUI가 왼쪽에서 slide 들어올때 view 를 만들고 오른쪽으로 슬라이드 아웃될 때 view 를 없애서 리소스를 잡아먹지 않는다.

이런 모든걸 Animation으로 할 수도 있지만 많은 단계를 직접 짜야되기 때문에 빌트인 transition을 사용하는게 추천된다.

View transition types

방금은 slide transition을 사용했다. 

slide transition은 leading edge 에서 시작해 trailing edge 로 빠져나간다.

이밖에 여러 다른 transition이 존재한다.

default transition은 뷰가 추가되거나 삭제될때 투명도를 조절한다.

.move(edge:) transition은 추가되거나 삭제될때 시작 또는 종료 지점으로 이동한다.

view 가 아래에서 위로 slide in 했다가 아래로 slide out 한다.

top은 반대이며 leading 은 slide 와 같고 trailing은 leading 의 반대다

slide 와 leading 이 같은거같은데 뭐지

움직이는것 말고 view 가 화면에 나타나는걸 animation하는 transition도 있다.

.scale()은 한 지점 에서 삽입될 때 view 가 확장되고 제거될 때 다시 축소된다.

전환에 대한 scale 비율을 파라미터로 지정할 수 있다.

scale 비율은 초기 view 의 크기비율을 정의하는데 0 은 기본전환을 나타낸다.

1 보다 작으면 view 가 해당 비율부터 커지고

1보다 크면 원래 view 보다 커진채로 시작해서 작아진다.

마지막 transition은 CGSize 로 off set을 정하거나 Length 값을 정해주는 것이다

그러면 정해진 offset애서 움직여 들어온다.

Extracting transitions from the view

animation과 같이 transition도 뽑아낼 수 있다.

struct 레벨에 추가하진 않고 extention을 통해 적는다.

Async transitions

SwiftUI를 사용하면보기를 추가하고 제거 할 때 별도의 전환을 지정할 수 있다.

combined(with:) modifier를 통해 두개의 transition을 동시에 쓸 수 있다.

preview 를 보면 insert 할때나 removal 할때 방식을 볼 수 있다.

Challenge

Challenge: Changing the flight details view

view 만들때 leading 에서 오고 숨길때 밑으로 fade away 하게 만들어라

button text transition 에서 text를 leading edge 에서 들어와서 없어질때 scale 을 통해서 사라지게 해라

728x90

'iOS > SwiftUI' 카테고리의 다른 글

Swift Enclosure vs Closure, ViewBuilder?  (0) 2020.10.27
#15 Complex Interfaces  (0) 2020.10.27
#16 Testing & Debugging  (0) 2020.10.26
SwiftUI in UIKit vs UIKit in SwiftUI  (0) 2020.10.26
SwiftUI size rendering algorithm  (0) 2020.10.26

댓글