iyOmSd/Title: Swift

[Swift] WWDC17 Advanced Animations with UIKit

냄수 2021. 8. 29. 17:04
반응형

애니메이션에 대한 몇가지 기본적인 것과 애니메이션은 어떻게 동작하는지 다루고

현대적 기법을 사용해서 애니메이션을 Interactive으로 만들어서 중단시키는 방법에 대해 논의하고

iOS11에서 제공하는 새로운 API에 대해 이야기하는 세션입니다!

그리고 애니메이션을 Coordinating하는 방법을 보일것이고 마지막으로 몇가지 팁과 트릭, 그리고 기술을 검토하여 훌륭한 애니메이션을 만들 수 있도록 도와준다 합니다

 

목차

Basic

Interactive and Interruptible Animations

New Property Animator Behaviors

Coordinating Animations

Tips and Tricks

 

 

Basic

X의 위치를 0에서 100으로 만들기위해 할 수 있는 방법은

UIView.animate를 호출하는 것입니다.

 

하지만

(세션기준 17년) 작년에(16년) 우리는 UIViewPropertyAnimator을 소개했고

이것은 우리가 더많은 애니메이션을 통제할 수 있게해줍니다.

 

사용자 정의 타이밍

상호작용가능한 애니메이션

중단할 수 있는 애니메이션

을 할 수 있다고 합니다

 

UIViewPropertyAnimator를 이용해서 애니메이션을 만들거고

startAnimation()을 호출합니다

이 함수는 애니메이션을 제작하는 애니메이션 블록을 실제로 실행하는 함수입니다.

 

 

 

타이밍 곡선이란 기본적으로 진행시간(Progress)이나 경과시간(Time)의 분수를 애니메이션의 진행과정으로 매핑하는 기능입니다.

(애니메이션의 빠르기와 타이밍을 나타냄)

linear 타이밍곡선은 시간과 진행시간의 분수가 같습니다.

 

 

linear말고도 내장된 타입인 easeOut, easeIn등 사용할수도 있지만

사용자정의로 만들어서 사용할 수 도 있습니다.

UICubicTimingParameters를 이용해서

2개의 cubic Bezier control points를 제공하면 됩니다.

이것은 뒤에서 어떻게 이기능을 제공할 수 있는지 알 수 있게됩니다.

 

 

 

Interactively Animating

사용자의 행동이 애니메이션 진행을 유도합니다.(사용자의 제스처에 따른 상호작용 애니메이션)

 

PanGestureRecognizer를 추가해서 애니메이션을 구현해볼 것 입니다

위의 보라색원을 사용자가 스크러빙하면 따라움직이는 동작입니다.

 

 

animator프로퍼티에 동작핸들러를 저장할 것입니다.

 

began

animator를 ease-out 타이밍으로 초기화 할것이고

그후에 즉시 pauseAnimation을 호출할 것입니다.

(기본적으로 그 애니메이션 속도를 0으로 설정하는 것임)

 

changed

애니메이션의 총거리와 비교해서 손가락이 이동하는 거리를 기준으로 animator의 분수를 완성해서 스크러빙 해보겠습니다.

(0에서 100까지 애니메이션을 하기때문에 100임)

 

ended

그리고 손가락이 떼지면 우리는 계속 애니메이션을 부를것 입니다.

 

여기서

우리는 흥미로운 순간이 존재하는데

일시정지할때멈춘뒤 계속 진행하려고 할때입니다.

 

Pausing

우리는 방금 animator를 만들었고 상호작용을 할 수 있도록 잠시 멈추려고합니다.

무슨일이 일어나는지 봅시다

 

animator는 active상태가 되고

자동으로 linear곡선으로 변환시켰습니다.

 

왜 그랬을까요?!

 

시간과 진행의 분수가같은 linear의 특성때문에 스크러빙 애니메이션을 모두 균일하게 할 수 있습니다.

(스크러빙은 원을 잡고 드래그하는 동작)

 

다시 애니메이션을 계속하면 어떤 일이 일어나는지 볼까요?(.changed실행)

위의 보라색원을 손가락으로 스크러빙하면 아래에 그래프의 원이 같이 움직임 - 아래의 원은 애니메이션을 설명하기위한 그래프

스크러빙 해서 애니메이션하고 있고

 

이제 손을 떼면(.ended실행)

 

 

우리는 continueAnimation을 호출합니다.

 

다시 ease-out타이밍 그래프로 변환되지만(직선에서 곡선그래프로 변경)

흥미로운 일이 일어났습니다.

 

 

50%였던 위치가 이제는 10%로 되었습니다.

이유는 다시 ease-out으로 전환할때 울리의 진행 값을 유지하기위해서 입니다.

여기서 durationFactor프로퍼티에 주목하고 싶습니다.

이것은 0으로 들어왔고 animator가 남은 시간을 사용하도록 하는 것입니다.

이경우엔 원래지속시간의 90%가 될것입니다.

예를들어 2초짜리 animator를 만들었다면 1.8초동안 지속될 것입니다.

 

인터렉티브 애니메이션은 이렇게 작동합니다.

 

Interruptible Animations

사용자의 동작이 현재 실행중인 애니메이션을 중단하거나 일시중지하는 것입니다.익숙한 예로는 사파리가있습니다.손가락을 튕기면 가속이 되고 그다음 천천히 감속되지만 스크린을 도중에 만진다면 그 애니메이션을 중단 시킬 수 있습니다.

 

위의코드에 약간의 변화를 줘서 인터렉션과 인터럽트가 모두 가능하게 만들어 보겠습니다.

animateTransitionIfNeeded이라는 새로운 함수를 작성했고 이 함수가 하는일은 실행중이 아니라면 전환이 시작됩니다.

progressWhenInterrupted프로퍼티는 인터럽트되기전에 animator가 수행한 상대적 진행을 저장합니다.

 

began

제스처가 시작되면 우리는 animator을 만들지만 이번에는 오직 전환이 실행되지 않을때만 만들것입니다.

그런다음 일시정지하여 인터렉션할 수 있도록 하고 중단되기 전에 상대적인 진행도를 저장합니다.

 

changed

손가락을 움직이면 animator의 분수가 완성된다.

하지만 이번에는 움직인 거리를 기준으로 animator의 분수를 움직임

 

ended

그리고 손가락을 들어올리면 우리는 애니메이션을 계속할것입니다.

하지만 이 예제를 조금더 흥미롭게 하기위해 ease-out타이밍함수로 계속하겠습니다

 

애니메이션이 단지 어떤기능을 하는지 ease타이밍 함수로 생성된다고 가정해보자

 

애니메이션 진행중에 pause호출

 

실행이 정지되고 새로운 함수가 생성됩니다.

 

현재 진행도에 맞게 시간을 변환하고

 

다시 재생할때는 원하는 타이밍 커브를 선택할 수 있습니다.

 

해당 함수에 맞게 진행률에 따라 다시 시간이 조절됩니다.

 

애니메이션을 중지하고

시간분수는 약 50%인 절반정도의 시간이 실행되었습니다.

ease-out로 전환해보면 진행도는 10%정도밖에 실행되지않았습니다.

 

pause를 하면

스크럽을 쉽게하기위해 linear타이밍 함수로 변환시킵니다.

애니메이션이 뛰지않도록 진행을 안정시키기위해 시간을 다시 맵핑합니다.

continueAnimation을 호출할때 우리는 ease-out타이밍 함수가 다시 될것이고

이때 우리는 새로운 타이밍함수로 다시 변환 시킬 수 있습니다.

 

 

 

새로운 API에대해 소개합니다.

New Animator Behaviors

iOS11의 새로운 프로퍼티로

scrubsLinearly

pausesOnCompletion이 있습니다.

 

위에서 말한 예시에서 우리는 animator를 멈췄을때 타이밍 기능을 linear하게 변환했고

이것은 animator를 쉽게 스크러빙 할 수 있게 해줬습니다.

하지만 때때로 인터렉션을 유지하는것이 유용할 떄가 있습니다.(linear로 변환하는게아닌 그대로의 함수를 사용하는것)

이제 그 작업을 scrubsLinearly를 비활성하여 그렇게 할 수 있습니다.

(예시로 linear, non-linear를 스크러빙하는 것을 보여주는데 투명도를 조절해서 차이를 보여줌)

(linear인경우 일정하게 투명해져서 끝까지 가기전까지는 보여지는 애니메이션이지만

non-linear인경우 초반에만보이고 중반부터 아예안보이면서 보다 더 자연스러운 애니메이션을 보여줌)

 

그리고 이제 애니메이션 완료시 일시중지를 할 수 있습니다.

애니메이션이 끝나면 자동으로 비활성 상태로 전환됬었습니다.

비활성 상태이기때문에 이전에 추적하던 어떤 애니메이션을 해제하게 될 것이고

끝난후 그것을 조작하거나 뒤집을수도 없다는 의미입니다.

 

하지만 이제 pausesOnCompletion을 true로 설정하면 animator는 100%상태(완료상태)로 정지한 상태에 있고

미래의 어느시점에서든 애니메이션을 되돌릴수 있게 됩니다.

 

pausesOnCompletion일때 animator의 completion handler가 불리지않습니다.(끝나지 않았기때문에)

따라서 언제끝나는지 알고싶다면 "running"프로퍼티를 관찰해서 알 수 있습니다.

 

 

Springs

애니메이션에 현실감을 더해주는 기능입니다.

critically damped springunder damped spring 두종류를 제공

critically damped spring은 빠르게 가속되고 빠르게 감속

under damped spring은 목표값을 넘어서 빠르게 가속한다음 진동

 

스프링 애니메이션은 독특하고 우리는 타이밍곡선에대해 생각해야합니다.

 

첫번째로 스프링 애니메이션이 큐빅애니메이션으로 변환하는것은 정의되지 않을 수 있으며

이는 큐빅타이밍함수는 최소값과 최대값에의해 제한되어있기 때문에 그 값을 진동시키거나 오버슈팅 하지않기때문입니다.

 

두번째이유는 독특한dx, dy요소로 2차원 속도로 애니메이션을 한다면 그것을 분해하고 두개의 스프링애니메이션을 만들것입니다.

이 2개의 애니메이션은 다른 속도를 가지고있기 때문에 동기화가 해제 될것이고 그래서 그것을 큐빅애니메이션으로 변환할 수 없습니다.

 

만약 당신이 스프링애니메이션을 중단해야한다면 몇가지 모범사례가있습니다.

스프링 애니메이션을 중단하고 현재 표현되는 값을 이용해서 새로운 애니메이션을 만드는것

두번째로는 초기속도없이 critically damped spring을 사용하는 것

이것은 오버슈팅이나 진동하지 않기때문입니다.

마지막으로 독특한 x,y성분으로 2차원 초기속도로 애니메이션을 한다면 그것을 분해하고 x애니메이션과 함께

하나의 animator 그리고 y애니메이션과 하나의 animator를 구동하는 것을 고려할 수 있습니다.

 

 

Coordinating Animations

 

이제 애니메이션을 조정하기위해 그 모든 지식을 어떻게 사용하는지 이야기해볼겁니다.

앱이있고 댓글버튼이 하단에 고정되어있고 이를 누르면 댓글뷰가 표시됨

인터렉션과 인터럽트기능을 이용해서 어떻게 하는지 보여드리고자합니다.

 

먼저 두개의 제스처를 추가합니다

tap제스처는 뷰가 확장하고 다시 사라질 것입니다.

애니메이션이 실행되는 중에 그것을 눌러서 reverse될 수 있기를 원합니다

그리고 pan제스처는 인터렉션을 할 것입니다. (탭은 창을 바로띄우고 내리는역할, 펜은 드래그로 끌어서 열고닫는 역할)

 

 

runningAnimators

먼저 UIViewPropertyAnimator인스턴스를 실행중 애니메이터 컬랙션으로 대체하는 것입니다.

UIViewPropertyAnimator 컬랙션 프로퍼티에 UIViewPropertyAnimator가 생성될떄 해당 컬렉션에 추가할 것이고 애니메이션이 끝나면 자동으로 자동으로 제거된다고 가정

 

animateTransitionIfNeeded

runningAnimators가 비었다면 현재 실행중인 전환이 없다는걸 의미하기때문에 우리는 전환을 시작할 것입니다.

그리고 우리는 critically damped spring을 사용할 것이다.

그리고나서 애니메이션을 수행할 것이고 그 실행중인 애니메이터를 컬랙션에 추가할 것이다

 

animateOrReverseRunningTransition

다음으로 텝제스처 인식기에서 우리는 이 핸들러를 부를 것이고

이함수는 애니메이션을 실행하거나 애니메이션을 reverse시킬 것입니다.

전환을 하고있지않다면 전환시작 시킬것이고 그렇지않다면 모든실행중인 애니메이션을 reverse시킬 것입니다

 

startInteractiveTransition

세가지 방법을 통해서 우리는 pan제스처 핸들링 코드를 추가할 것입니다.

제스처가 시작될때 호출되며 실행되지않으면 전환을 시작하고 당신의 애니메이터를 모두 균일하게 일시중지하고 그들이 만든 상대적인 진행률을 저장할 것입니다

 

updateInteractiveTransition

손가락이 이동한 거리와 애니메이터가 중지되기전 진행한 정도에 따라서 애니메이터가 균일하게 스크러빙할것입니다.

 

continueInteractiveTransition

마지막으로 손가락을 들어올리면 손가락이 이동하던 방향을 기준으로 조건적으로 reverse하는 모든 애니메이터들에게 애니메이션을 계속하라고 부릅니다.

 

더 흥미롭게 블러를 추가해봅시다

 

 

iOS8에서 UIVisualEffectView를 소개했는데 이것은 view hierarchy에 흐릿하고 진동을 줄 수 있게해줍니다.

UIVisualEffectView는 애니메이션 가능한 특성을 가지고있기때문에 훌륭합니다.

 

 

단지우리는 블러를 위한 새로운 애니메이터를 만들고 critically damped spring을 사용할 것입니다.

그리고나서 애니메이션을 블러효과를 설정하거나 해제하거나 블러애니메이터를 시작하고 컬랙션에 그것을 추가할 것입니다.

 

(새로운 창을올리면 뒤의 화면이 블러처리되는현상을 보여주면서)

 

슬로우 모션으로 다시 한번 보죠

이 블러애니메이션을 잘 느끼고있는지 모르겠습니다.

너무 빨리 애니메이션되서 보여지는것 같다고 느껴지고 좋아보이지 않습니다.

 

이것에는 몇가지 이유가있습니다.

첫번째로 critically damped spring을 사용하고 있기때문에 블러는 너무빨리 애니메이션화 합니다.

그리고 animator가 linear스크러빙할 것이기때문에 여전히 블러는 처음에 약간빨리 애니메이션화 될것이고

나중에 느리게 애니메이션화 할 것입니다.

 

이문제를 해결하기위해서

custom timing curve를 제공할 것입니다.

ease-in함수는 천천히 블러애니메이션 되고 ease-out은 정말빨리 애니메이션합니다.

이것들은 서로반전이기떄문에 대칭적인 값을 받을것입니다.

즉 나갈때와 오늘길의 경로가 일치할 것입니다.

 

scrubsLinearly를 비활성화했고

CubicTimingParameters를 생성했습니다.

 

(다시 애니메이션을 동작하면서)자, 다시확인해보겠습니다~!! 훨씬좋네요~!

 

 

View Morphing

애니메이션을 조금더 흥미롭게 해봅시다.

 

Scaling, translation, and opacity blending of two views

(두 뷰의 크기, 이동, 투명도를 혼합해서 변형함)

(확장했을때 버튼이 타이틀로 되면서 커지는 애니메이션 효과를 주려고함)

 

comments가 커짐

 

먼저 해야할일은 크기를 계산하는것입니다.

단지 목표치수와 현재치수에 기초한 단순한 비율입니다.

이것들중 하나를 계산하면 다른하나는 반대로 가져가서 공짜로 얻을 수 있습니다.

 

위치를 계산하는건 더흥미롭습니다.

우리가 크기를 애니메이션화 하고있기 때문입니다.

크기는 y값에 영향을 줄 것이기떄문에 새로운 값을 얻기위해 크기를 미리 적용하고 translation에 사용할 수 있습니다.

 

3개의 animator를 사용해서 구현해보겠습니다.

critically damped spring애니메이션으론 전환을,

easein으론 뷰를 띄울때 transition을

easeout으로 뷰를 없앨때 투명도 애니메이션을 할 것이고

이것들 모두 linear scrubbing은 비활성화해놨다

 

라벨 변환을 애니메이션화 했습니다.

inLabel은 identitiy변환을하고

outLabel은 미리 크기를 계산하고 bounds에 맞게 이동합니다.

outLabel은 inLabel의 경계와 일치하도록 변형될 것이고

두개의 애니메이터를 만들어서 알파값을 변경하고 linear scrubbing을 비활성화 하여 pacing(타이밍함수)을 유지할 수 있도록 했습니다.

 

쉽게 타이밍 특성을 선언하고 동작을 스크러빙하는 것만으로도 이러한 효과를 달성하기위해 property animator를 사용할 수 있습니다.

 

 

Tips and Tricks

코너를 애니메이션화하기위해

CALayer에 cornerRadius를 추가했고

 

상단의 모서리만 애니메이션화 할 수 있도록

CALayer에 maskedCorners 프로퍼티를 추가했습니다.

 

모든 애니메이터가 같은 단위기간을 공유하는 것이 정말 중요합니다.

스크러빙하는것을 쉽게 만들고 균일하게 스크러빙하는 것을 가능하게합니다.

 

하지만 때때로 조금일찍 끝나는 애니메이션이나 지연으로 시작하는 애니메이션을 갖는것이 유용할 때가있습니다.

UINavigationBar도 이러한 효과를 가져왔습니다.

 

지연인자나 시간이 줄어든 애니메이터를 만들수도있지만 더 우아한 방법이있습니다다.

두가지 키프레임 방법을 볼 수 있습니다.

 

상대적인 시간이 0인 UIView.animateKeyframes을 호출하고

외부 애니메이터의 지속시간을 상속받을 수 있습니다.

중첩으로 애니메이션해놓으면 무료로 상속받을 수 있습니다.

그래서 만약 property animator를 사용하지 않는다면 정말 유용할 수 있습니다.

(몇 초뒤 정적인 시간이아니라 해당 애니메이션에 상대적의 시간을 지정해서 사용할 수 있음)

 

expand인경우 늦게 시작되기를 원합니다.

우리는 0.5의 상대적인 시작시간을 사용할 것이고 0.5의 상대적인 지속시간을 사용할것입니다.

 

collapse인경우 우리는 0의 상대적인 시작시간을 사용할 것입니다.

왜냐하면 즉시시작되기를 원하고 0.5의 상대적인 지속시간을 이용하여 일찍 끝나게 하기 때문입니다.

 

두가지 변수에 관심을 기울이고 싶습니다.

keyframe animation에는 linear타이밍함수를 사용할 필요가 없습니다.

custom타이밍을 포함하여 모든 타이밍 기능을 사용할 수 있습니다.

 

 

Additive Animations

이렇게하면 10바퀴를 돌까요?

(10바퀴 돌아가는 사각형을 보여주면서)

 

아닐것입니다.

코어애니메이션은 당신의 관점변화를 애니메이션할때 전체변위에만 신경쓰기때문에

이경우 20 * pi값은 현재 회전값과 동일한 방향이고 전체 변위는 실제로 0이기때문에 가만히 있을것입니다.

 

또한 반시계방향으로 180도를 회전시키려면 비슷한 문제가 발생합니다.

 

전체변위만 신경쓰기때문에 가장 짧은 경로를 찾을 것이고 여기서 애매하다면 시계방향으로 나타날것입니다.

반시계로 돌아갈것을 기대했지만 시계방향으로 돌아갈것입니다.

그러면 어떻게해야할까??

 

우리만의 코어 기본 애니메이션을 만들고 수동으로 가치를 설정하면 아주 효과가 있을 것입니다.

하지만 우리는 property animator로부터 추적과 scrubbing행동을 얻지못할 것입니다.

우리가 할 수 있는 것은 몇개의 작은 추가회전 애니메이션으로 분해해서 모두 애니메이션화해서 효과를 만들어 낼 수 있다는 것입니다.

 

transform은 frame, bounds, center, position와 함께 부착되는 한 애니메이션 가능한 특성인 것으로 밝혀졋습니다

그래서 해결책은

우리는 실제로 20개의 애니메이션을 만들 것입니다.

각 애니메이션은 180도 애니메이션을 만들 것이고 그것을 모두 다음 애니메이션에 기여할 것입니다.

 

멋진 것은 실제로 20개의 애니메이션이 동시에 작동한다는 것입니다.

우리가 애니메이션을 디자인하고 작곡하고 만들 때 조금 더 추가적으로 생각하도록 도와줍니다.

거나 함께 작곡하여 흥미로운 전환을 만들 수 있는지 고려하는것입니다.

반응형