-
Enhance your UI animations and transitions (feat. WWDC 2024)SwiftUI 2024. 7. 29. 18:33
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 WWDC 2024에서 소개된 UI 애니메이션과 전환 기능을 향상시키는것에 대해 알아보겠습니다 🙋🏻
그럼 바로 본론으로~ 🏃🏻
Enhance your UI animations and transitions
소개에서는 이제 SwiftUI, UIKit 및 AppKit의 애니메이션의 상호작용이 더 극대화되었다고 합니다.
아마 유추해보건데, 각 기술들에서 애니메이션을 자유롭게 상호적으로 쓸 수 있는것에 대해 알려줄것 같네요 🤔
Transitions
iOS 18에선 새로운 확대/축소 전환 기능이 생겼습니다.
이렇게 특정 셀을 가지고 화면이 전환될때 확대/축소 애니메이션이 적용되고 또, 드래그도 마음대로 할 수 있죠.
즉, 동일한 UI 요소를 화면에 유지하여 앱의 연속성을 높일 수 있습니다.
코드로 볼까요?
이런식으로 축소 전환을 구현하기 위해서 두가지 작업을 수행해야 합니다.
먼저, navigationTransitionStyle 뷰 모디파이어로 .zoom 케이스를 가지고 확대/축소를 수행하도록 적용합니다.
그리고, matchedTransitionSource 뷰 모디파이어를 연결해 시스템이 어느 뷰에서 확대/축소할지 알 수 있도록 합니다.
두 모디파이어 모두 보면, 동일한 식별자(id)와 네임스페이스(braceletList)를 지정해 SwiftUI가 어떤 프리뷰가 어떤 제시된 뷰하고 같이 연결되어 가는지 알 수 있도록 해주는것입니다.
그럼 이렇게 확대된 전환 효과를 가질 수 있죠.
SwiftUI말고 UIKit으로도 확대/축소 전환을 채택하는 방법 또한 비슷합니다.
preferredTransition을 이용합니다.
SwiftUI처럼 zoom으로 지정하면 되죠!
클로저에선 확대/축소 전환에 사용할 뷰를 제공해주면 됩니다.
여기선 확대/축소 시 bracelet 식별자를 캡쳐해 사용하고 있습니다.
즉, 뷰를 직접 캡쳐하는 대신 뷰를 가져오는데 사용할 수 있는 Bracelet 모델 개체이죠.
컬렉션 뷰와 같이 소스 뷰를 재사용할 수 있는 경우에 매우 중요합니다.
내부에서 클로저에 전달된 context를 사용하면, 아래처럼 뷰를 빠져나가지 않아도 해당 셀 사이를 스와이프 할 수 있습니다.
요렇게 말이죠ㅎㅎ
이 확대/축소 API는 SwiftUI와 UIKit 모두에서 fullScreenCover 및 시트 프레젠테이션 API와 함께 작동합니다.
그럼 우선, UIKit에서 이런 새로운 유동적인 전환이 뷰 컨트롤러 생명 주기 및 appearance callback과 어떻게 상호 작동하는지 알아보시죠 😃
UIKit의 뷰 컨트롤러 생명주기를 알고 있다고 가정하면, 그냥 한번 톺아보는 느낌정도로 새로운 소개는 없어요.
다들 알고 있는 VC 라이프싸이클 그걸 설명하고 있습니다.
그렇기에, 굳이 이 부분은 하나씩 다루지 않고 넘어갈께요 ☺️
중요한건, 이 부분입니다.
이렇게 빠져나가려고 드래그를 하는 도중이면, 팝 전환이 시작될 때 해당 뷰가 사라지는 상태인 viewWillDisappear가 불리고 이동합니다.
근데, 이상태에서 빠져나가지 않고 드래그를 놓으면 곧바로 VC가 appearing 상태로 이동하죠.
그리고, 곧바로 루프를 한번 돌아 Appeared 상태로 전환됩니다.
이것이 콜백 타이밍에 대한 기존 전환 시나리오였어요.
다만, 반대로 푸시 도중에 뒤로 탭하거나 스와이프하여 팝을 해도 우선 해당 다음 뷰가 나타난 Appeared 상태를 거칩니다.
즉, 나타나긴 한거죠.
그 후, 곧바로 Disappearing을 거쳐 pop되었으니 Disppeared가 되는것입니다.
즉, push와 pop의 방법이 다르죠.
이건, 의도된것인데, 개념적으로 시스템은 중단된 push를 취소하지 않습니다.
대신, push는 항상 pop으로 변환됩니다.
살펴보았듯, push된 뷰 컨트롤로는 항상 Appeared 상태에 꼭 도달합니다.
이 새로운 전환 효과에 대해서는 기존 코드에 쉽게 적용할 수 있습니다.
소개하는 팁은, 이렇게 전환이 진행중인 경우 탭 핸들러는 푸시를 호출하지 못하기에 요렇게 사용하지 말라고 합니다.
즉, 전환이 실행중인지 여부에 관계없이 푸시를 호출하면 됩니다.
또한, 임시 전환 상태를 최소한으로 유지해야 합니다.
이렇게 isTransitioning 상태를 굳이 넣지 않아야해요.
상태가 적을수록 다른 코드가 전환 상태에 종속될 가능성이 줄어들죠.
만약 전환 중 상태를 변경해야 한다면, viewDidAppear나 viewDidDisappear로 재설정하는걸 권장합니다.
왜냐하면, 전환이 끝날 때 호출되는것을 보장해주기 때문입니다.
SwiftUI animation
SwiftUI는 일반적으로 지속적으로 변화하는 상태를 처리하는데 더 적합합니다.
이젠 SwiftUI 애니메이션으로 UIKit 및 AppKit 뷰에 애니메이션을 적용하기 위한 새로운 API들이 생겼습니다.
이렇게, 각 기술별로 동일한 애니메이션을 구현하는것이 있습니다.
UIKit이나 AppKit에선 기존 애니메이션 API를 사용해 호출 매개변수에 spring 애니메이션을 설정하고 클로저에서 뷰 속성을 업데이트하죠.
SwiftUI는 애니메이션 타입으로 애니메이션을 설정하고 클로저에서 상태를 업데이트하죠.
유사한데, 두가지 장점을 모두 가져가면 어떨까요?
iOS 18에서부터는 가능합니다!
SwiftUI 애니메이션 타입을 사용해 UIKit / AppKit 뷰에 애니메이션을 위와 같이 적용할 수 있습니다.
이를 통해 SwiftUI CustomAnimations를 포함한 전체 SwiftUI 애니메이션 타입을 사용해 UIKit 뷰에 애니메이션을 적용할 수 있는것이죠 😁
기존 UIKit API는 CAAnimation을 생성한 다음 뷰 레이어에 추가합니다.
그러나 SwiftUI 애니메이션은 CAAnimation을 생성하지 않고 뷰 레이어의 프레젠테이션 값에 직접 애니메이션을 적용하게 됩니다.
Animationg representables
이제 SwiftUI에 UIKit의 애니메이션을 적용해볼까요?
우리가 익숙한, UIKit의 컴포넌트를 SwiftUI 뷰에서 사용하려면 이렇게 UIViewRepresentable을 이용했죠.
여기서 SwiftUI 뷰에 존재하는 isBeadBoxOpen 프로퍼티를 이용해 하위 BeadBoxWrapper 뷰의 isOpen 바인딩 값을 통해 애니메이션을 주려면 이렇게 하면 되죠.
Wrapper View에서는 context에 animate를 심어주고, body View에선 해당 바인딩 변수에 animted 메서드를 호출시켜주는것이죠.
만약, 커스텀하게 UIKit 애니메이션을 만들어 SwiftUI에 제공하고 싶다면, UIGestureRecognizerRepresentable을 커스텀 애니메이션 구체 타입에 채택하여 구현해주고 사용할 수 있습니다 😃
SwiftUI 뷰와 UIView에서 실행되는 단일 애니메이션은 완벽히 동기화되어 실행됩니다 😃
Gesture-driven animations
이젠 단일이 아닌 동일한 API가 연속된 동작으로 실행될 때 어떤 강점을 가지는지 보겠습니다.
똑같은 동작을 구현한다 하더라도, SwiftUI 애니메이션은 동일한 효과를 나타내기에 이미 기능들을 쉽게 구현해놓고 있습니다.
즉, UIKit에선 ended에서 애니메이션화 하기 위해 현재 속도를 기반으로, 스프링의 초기 속도를 계산하고, 현재 위치에서 최종 위치까지 이동하는 거리로 나누어 단위 속도로 변환해야 하는 번거로운 작업이 있죠.
근데, SwiftUI는 그런 속도 보간등의 기능이 이미 있기에, 단순히 제스쳐에 병합해주면 되는것이죠.
그렇기에 우리는 이제 UIKit에서 SwiftUI 애니메이션을 적용할 수 있기에, 아래와 같이 쉽게 사용할 수 있죠.
즉, 이렇게 여러 연속된 애니메이션을 도입할 수 있는것이죠!
마무리
핵심은, 확대/축소 전환 효과도 있지만 SwiftUI와 UIKit & AppKit의 상호작용이 가장 큰것 같아요.
특히, SwiftUI와 UIKit의 기술을 같이 사용하는 환경에서는 애니메이션 처리가 까다로웠는데 정말 많이 해소된것 같습니다 👍
레퍼런스
'SwiftUI' 카테고리의 다른 글
SwiftUI - ScaledMetric (3) 2024.09.09 SwiftUI - AnyLayout (4) 2024.08.29 Create custom visual effects (feat. WWDC 2024) (84) 2024.07.22 SwiftUI - ViewThatFits (73) 2024.07.15 Demystify SwiftUI containers (feat. WWDC 2024) (76) 2024.07.11