본문 바로가기

SwiftUI

[SwiftUI] 뷰(View)가 여러개인 경우 효율적인 애니메이션 스케쥴링 방법

안녕하세요

 

iOS공부를 하고있는 주니오스라고 합니다.

 

최근에 제가 혼자 프로젝트를 진행하고 있는데요. 한 스크린에 여러가지 뷰들이 상화작용하여 움직이는 형태를 구현하려 했습니다.

 

각 뷰의 애니메이션 상태를 중앙집중식으로 관리하기 위해 아래 사진과 같은 구조를 선택했습니다.

애니메이션 바인딩 구조

 

 

이구조의 장점은 다음과 같습니다.

 

  • View들 담고있는 Screen에서 상태변화를 발생시킬 경우 바인딩을 통해 뷰들을 관리할 수 있다.
  • 구조적인 애니메이션 구현이 가능하다.

뷰의 상태를 관리하기 위해 각 뷰마다 자신의 애니메이션 상태를 저장하는 ViewState타입이 존재합니다.

 

아래 코드는 실제로 구현한 ViewState코드 입니다.

 

 

 

아래코드는 Screen코드의 일부입니다.

 

State랩퍼로 각 뷰의 상태를 저장하고 Binding관계를 형성한 것을 확인할 수 있습니다.

 

 

 

 

ScreenState가 변경될 경우 아래 코드를 보시면 각기다른 함수를 호출하는 것을 확인할 수 있습니다.

함수를 자세히 보시면 ViewState에 저장된 animTime 연산 프로퍼티를 활용하는 것을 확인 할 수 있는데요

 

이렇게 ViewState타입에 해당 애니메이션에 대한 정보를 저장하면 Screen에서 편하게 스케쥴링을 할 수 있니다.

 

자, 이렇게 구성한 화면이 어떻게 동작하는 지 직접 확인해 보시죠!😆😆

 

 

2개뷰의 애니메이션조율

 

상태변화에 효율적으로 대체하기

애니메이션 실행동작이 한번에 여러개가 들어오면 어떻게 될까요?

 

실행중이던 애니메이션이 끊겨 다소 부자연스러운 동작이 발생할 수 있습니다.

 

저는 이 문제를 해결하기 위해 뷰내부에 뷰만의 스케쥴링 기법을 구현하였습니다.

 

 

아래코드는 앞서 보여드린 GIF내 앱의 타이틀(이름)을 표시하는 뷰입니다.

 

isAnimationWorking이라는 프로퍼티를 주목해 주세요.

 

isAnimationWorking 프로퍼티는 애니메이션 실행시 true로 설정되고

 

애니메이션 진행 시간이 지난 후 animWorkingOn함수에 의해 false로 설정됩니다.

 

ViewState의 상태가 변경되어도 해당 프로퍼티 값에 따라 애니메이션 실행을 보류합니다. 

 

실행이 보류된 상태는 nextState에 저장되는 것을 확인할 수 있습니다.

 

nextState에 저장된 상태는 isAnimationWorking프로퍼티의 변화에 따라 두번째 onChange수정자에 의해 viewState에 할당됩니다.

 

하지만 이렇게만 구현하면 문제가 발생하지요

 

 

상황을 가정해 보겠습니다.

 

idle상태에서 upward상태로 변경된 직후 다시 idle상태를 요청했다고 가정하겠습니다.

 

- 첫번째 상태 -

viewState = .idle

isAnimationWorking = false

nextSatae = nil

 

- 두번째 상태 -

viewState = .upward

isAnimationWorking = true

nextSatae = nil

 

- 세번째 상태 -

viewState = .idle

isAnimationWorking = true

nextSatae = .idle

이상태에서 viewState변화에 따른 onChange는 무시됩니다.

 

- 네번째 상태 - 

viewState = .idle

isAnimationWorking = false

nextSatae = nil

isAnimationWorking이 변경되어 두번째 onChange의 클로져가 호출됩니다.

 

하지만

 

viewState = nextSatae 배정연산이 발생하여도 onChange가 호출되지 않아 idle상태로 전환되지 않습니다.

 

왜냐하면 viewState는 세번째 상태와 네번째 상태에서 동일하기 때문입니다.

 

따라서 애니메이션 실행중에 새로운 상태가 요청될 경우를 표시할 수 있는 새로운 상태가 필요했습니다.

 

그것이 ViewState타입 코드에서 확인할 수 있는 congestion상태입니다.

 

 

 

감사합니다~!!

 

전체코드 정보는 아래와 같습니다. 🥹

https://github.com/J0onYEong/Project-Helper/commit/7052cdcf65ec51cad939cdd04519ff15982610c8