 Apple Lover Developer & Artist

영속적인 디자인에 현대의 공감을 채워넣는 공방입니다

 Apple/Stanford iOS Programming (SwiftUI)

Lecture 7 Review Part 2: ViewModifier Animation

singularis7 2021. 9. 9. 18:36
반응형

지난 시간에 카드에 대해 UI Animation을 구현해보기 위해 ViewModifier로 재정의 해보 았다. 애니메이션 동작 원리 및 구현 방법을 배우기 전에 몇가지 애니메이션의 황금률에 해당되는 기본원칙 몇가지를 배워보자!

가장 먼저 이해해야 할 것은 오직 변경사항만 애니메이션 할 수 있다. 아무것도 변하지 않는다면 애니메이션을 구현할 수 없다. 애니메이션을 구현할 수 있는 변경 사항에는 무엇이 있을까?

  • ViewModifier에 대한 인수
  • Shapes 생성에 대한 인수
  • UI에 View의 존재 여부

애니메이션이 이미 완료된 사용자 변경 사항을 보여준다는 점이다.

여튼 어떻게 애니메이션을 구현할 수 있는가?

  1. 암묵적으로 애니메이션을 파라미터로 사용하는 .animation이라는 특수 ViewModifier를 사용하는 것 (.animation(Animation))
  2. 명시적으로 .withAnimation(Animation) { } 을 사용하는 방법이 존재한다. 
  3. 마지막으로 UI로부터 View를 포함시키거나 제거하는 것 또한 독립적으로 애니메이션 할 수 있다.

Implicit Animaiton

  • "Automatic animation" 이라고도 부른다.
  • animation modifier를 수행하는 모든 ViewModifier 인수는 항상 애니메이션 된다.
  • 변동 사항은 설정된 duration과 curve에 따라 에니메이션 된다.

scary 혹은 upsideDown에 변동사항이 발생되면 opacity 와 rotation effect를 주는 애니메이션을 발생시키는 코드이다. opacity는 점점 사라지는 효과를 줄 것이고 rotation은 실제로 거꾸로 회전하는 뷰를 보여줄 것이다.

애니메이션 Modifier는 Container에서 잘 동작하지 않는다. Container 내부의 View에 .anmation 효과를 전파시키기 때문이다. 

 .animation의 파라미터로 넘어가는 것은 Animation 구조체이다. 애니메이션에 관하여 컨트롤 할 수 있는 요소를 살펴보자!

지속시간, 지연시간, 애니메이션 반복 여부 그리고 Curve 를 정의할 수 있다.

여기서 애니메이션의 커브가 무엇일까? 애니메이션은 시간이 흐르면서 발생한다. 애니메이션의 수행시간이 1, 2, 0.5초 등 다양하게 존재할 수 있다. 시작 지점에서 끝 지점까지 얼마나 빠르게  변화하는 가를 의미한다. (durarion 동안의 speed 변화)

  • 만약 선형적으로 구현되었다면 일정한 비율로 애니메이션이 회전, 페이드 등의 동작할 것이다
  • ease(In\Out)은  애니메이션을 약간 느리게 시작한 다음 속도를 높혀서 진행한 후 끝으로 갈수록 다시 느려지고 시작과 끝에서 조금더 안정적으로 바뀐다.
  • spring은 정상적으로 애니메이션을 시작하지만 끝에 도달하면 스프링 처럼 동작한다.

automatic implicit animation은 보통 animation 행위에 대한 주요한 원인이 아니다. implicit animation은 컨테이너가 아닌 작은단위의 leaf View 혹은 일반적으로 독립적인 형태로 동작하는 View에 사용한다

보통 animation 이 발생하는 원인은 Model에 변동사항이 생기거나 사용자의 어느 행위에 응답하는 것이 존재한다. 이러한 변동사항에 대하여 애니메이션을 구현할 때에는 explicit animation 을 통해 구현한다.

명시적인 에니메이션은 ViewModifier 및 shape에 대한 모든 적합한 변경 사항이 지정한 애니메이션 곡선을 사용하여 함께 애니메이션 되는 애니메이션 트랜잭션을 생성한다. 

명시적 애니메이션을 사용하는 방법은 다음과 같다.

파라미더로 duration, curve 등을 명시할 수 있다.

View를 전환할 때 사용되는 애니메이션을 Transition이라고 부른다. 비대칭적으로 트랜지션을 구현하여 화면에 등장할 때와 사라질 때 서로다른 효과를 갖도록 설정할 수 있다.

우선 데모를 따라해보면서 감을 잡아보자

목표는 이모지를 회전시키는 애니메이션 기능을 구현하는 것이다.

카드가 매칭되었을 때 rotation effect를 반영하도록 코딩한 후 코드를 실행시켜보았다. 실행결과는 아무런 변화가 없는 것처럼 보인다. 실제로는 360도 회전하였지만 애니메이션이 발생하지 않고 순식간에 변동되어 우리가 인지하지 못하였기때문이다. 

Rotation Effect
Rotates this view’s rendered output around the specified point. // 특정 좌표를 기준으로 회전 시켜준다

회전에 대하여 애니메이션을 구현하려면 어떻게 구현해야할까? 첫번째 방법은 명시적으로 애니메이션 시키는 것이다. 예를 들어 카드를 뒤집거나 이와 유사한 동작을 애니메이션으로 만들 것이다.

Implicit Animation 구현 방법을 사용해보자!

애니메이션이 동작한다! easeIn, easeOut, easeInOut, Spring 같은 여러 효과를 사용할 수 있으며 duration을 지정해주지 않으면 시스템 기본값을 사용하게 된다. 

제일 큰 문제점은 화면이 rotation 되면 이모지의 위치가 카드 밖을 벗어나서 카드 안쪽으로 움직이는 형태의 렌더링이 지속된다.

이 문제는 애니메이션을 구현할 수 없는 것에 대하여 애니메이션을 적용한 것과 관련있다고 한다. 실제로 이모지는 Text View 를 통해 보여지고 있지만 font modifier가 사용되었기 때문에 animatable하지 않다. 

다시말해 font는 애니메이션을 구현할 수 없는 modifier에 속한다. 

또한 화면의 방향을 바꿀 때 마다 여러 종류로 애니메이션이 동작하는 점을 알 수 있는데 왜냐하면 카드의 크기나 위치가 약간씩 변화하며  위치도 변화하기 때문이다. 어쨋든 고쳐야 한다.

Font 크기를 다른 방법으로 지정해보자!

고정 크기의 폰트 사이즈로 설정한 다음 scaleEffect를 통해 폰트 크기를 배수로 조정할 수 있다. 위 처럼 코딩하면 scale이 geometry 크기에 관계없이 고정값이 되기 때문에 조금 더 개선해보고자 한다.

Scale Effect 는 애니메이션을 구현할 수 있기 때문에 사용하는 것이며 코드는 다음과 같다.

위 코드에서 애니메이션 modifier 위치에 따라 애니메이션이 동작하는 view의 범위가 달라진다. font와 scale에 애니메이션 효과가 반영되지 않는 이유, ZStack에 애니메이션을 적용하면 내부에 animatable한 View에 모두 애니메이션이 반영되는 것 또한 이와 같은 맥락이다. 따라서 Combiner View에 애니메이션을 적용할 때에는 주의해야 한다.

또다른 문제가 있다면 매칭된 카드중 하나에서만 애니메이션이 구현되고 있다는 점이다. 

다시 애니메이션을 적용할 수 있는 범위에 대하여 복기해보면 "변동사항"에 애니메이션을 적용할 수 있다는 점을 떠올릴 수 있다. 두 카드 모두 isMatched가 변동되니까 여기에 맞추어 두 카드 모두 애니메이션 구현되도록 만들어보자

한가지 방법은 content를 꺼내는 것인데 isFaceUp이 동작하지 않는다는 단점이 존재한다. 

반응형