-
SwiftUI를 이용한 인터랙티브 애니메이션SwiftUI 2024. 11. 1. 18:50
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 제가 필요해서 구현해보다가 이런것도 SwiftUI로 쉽게 구현할 수 있구나 하는걸 소개해볼까 합니다 🙋🏻
바로 SwiftUI를 이용한 인터랙티브 애니메이션이 그 주제입니다ㅎㅎ
이번 포스팅은 개념적인 설명 그런것보다 실제 구현에 기반하고 있기에 코드를 주의깊게 보는것이 좋을것 같습니다! ⚠️
인터랙티브 애니메이션
인터랙티브가 뭘까요?
이런 뜻을 가지고 있다고 구글 AI, 재미나이가 말해주네요.
즉, 인터랙티브 애니메이션이라는것은 쌍방향의 애니메이션을 뜻하게 되는겁니다.
사용자의 입력이나 동작에 반응해 변화하는 동적인 애니메이션을 말해요.
그런 구현은 정말 무궁무진하게 생각한대로 펼쳐볼 수 있겠지만, 저는 하나의 예시를 가지고 그 인터랙티브 애니메이션 구현을 살펴볼까 합니다!
가장 먼저, 어떤것을 구현하려고 하는지, 시뮬레이터부터 보겠습니다.
요런거 해보려해요.
사용자가 두 선택지 중 하나를 선택하면 그 선택지의 로고가 중앙 배치되면서 커지고, 배경색은 해당 선택지의 색상으로 차게 되는것이죠.
그 와중에 해당 로고가 중앙으로 올때도 자연스럽게 애니메이션 효과가 적용되고 반대로 선택 받지 않은 로고는 자연스럽게 우측 하단으로 빠르게 사라지는 애니메이션 구현이 제 목표입니다!
그럼 우리가 좋아하는 코드로 파해쳐볼까요?
import SwiftUI struct ContentView: View { @State private var isTopViewSelected = false @State private var isBottomViewSelected = false var body: some View { ZStack { // 배경 if isTopViewSelected { Color.blue .ignoresSafeArea() } else if isBottomViewSelected { Color.orange .ignoresSafeArea() } VStack(spacing: 0) { // 상단 뷰 HStack { Spacer() Button( action: { withAnimation(.spring(response: 1.0, dampingFraction: 0.7)) { isTopViewSelected.toggle() isBottomViewSelected = false } } ) { Image("apple") .resizable() .frame( width: isTopViewSelected ? 300 : 100, height: isTopViewSelected ? 300 : 100 ) } .offset( x: isBottomViewSelected ? UIScreen.main.bounds.width + 100 : 0, y: isTopViewSelected ? UIScreen.main.bounds.height/4 : (isBottomViewSelected ? UIScreen.main.bounds.height + 100 : 0) ) Spacer() } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(isTopViewSelected || isBottomViewSelected ? Color.clear : Color.blue) // 하단 뷰 HStack { Spacer() Button( action: { withAnimation(.spring(response: 1.0, dampingFraction: 0.7)) { isBottomViewSelected.toggle() isTopViewSelected = false } } ) { Image("nvidia") .resizable() .frame( width: isBottomViewSelected ? 300 : 100, height: isBottomViewSelected ? 300 : 100 ) } .offset( x: isTopViewSelected ? UIScreen.main.bounds.width + 100 : 0, y: isBottomViewSelected ? -UIScreen.main.bounds.height/4 : (isTopViewSelected ? UIScreen.main.bounds.height + 100 : 0) ) Spacer() } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(isTopViewSelected || isBottomViewSelected ? Color.clear : Color.orange) } } .ignoresSafeArea() } }
되게 많아보이고 길어보이지만 별거 없고 집중해야할곳은 오프셋과 애니메이션 변화에요.
ZStack으로 선택된 배경 색상으로 덮어주기 위해 분기를 처리해 배경을 만들어줍니다.
선택된것이 없으면 해당 분기를 타지 않기에 배경이 없겠죠?
그리고 그 위에 VStack으로 두 선택지를 만듭니다.
로고를 버튼으로 만드는데, 로고를 클릭하면 해당 뷰가 선택되는 상태 변수를 토글 시키고 반대되는 상태 변수는 false로 처리합니다.
해당 로직을 스프링 애니메이션으로 적절한 response, 여기서 리스폰스는 작용 시간을 의미합니다.
짧게 주면 빠르게 애니메이션이 끝나고 길게 주면 천천히 애니메이션이 진행됩니다.
그리고 dampingFraction도 적절히 원하는 수치로 맞춰줘요.
dampingFraction은 스프링 애니메이션이 될때 어느정도로 통통 스프링처럼 튈것인가를 조정해줍니다.
즉, 탄성을 말해요.
보통 0.0~1.0 값을 사용하는데 값이 작을수록 더 많이 튕깁니다.
1.0을 초과하는 값을 주입하면 더 엇나가서 튕기거나 그런 현상이 있고 원하는 효과를 얻을 수 없으니 주의해야 합니다.
이렇게 애니메이션을 설정해주고 이제 로고가 클릭되었을때 조건으로 frame을 구분해줍니다.
현재 코드에선 선택된 로고는 3배 크기로 늘어나죠.
물론, 이 부분도 scaleEffect를 활용해도 됩니다.
.scaleEffect(isTopViewSelected ? 3 : 1)
그리고 이제 가장 중요한 오프셋을 설정해줘야 합니다.
x 오프셋 값은 현재 중앙 정렬이니 건들일 필요가 없지만, 다른 선택지가 선택되었을때 우측 하단으로 사라지기 위해 현재 스크린 넓이에서 100만큼 더해 변경해줍니다.
그리고 y 오프셋 값은 현재 선택지가 선택되면 가운데로 정렬시켜야 되는데요.
각 상/하단 뷰는 디바이스의 크기를 정확히 반으로 가지고 있어요.
그리고 로고는 거기서 또 중앙을 차지하니 화면 전체에서 보면 디바이스 높이의 1/4 지점이 되는곳에 위치하고 있는것이죠.
그래서 선택이 되면 전체 디바이스 높이의 1/4 만큼 상단은 아래로 하단뷰에서는 위로 옮겨주면 중앙 배치가 됩니다.
선택 받지 않은 선택지는 우측 하단으로 사라져야 하기에 디바이스 높이에서 적절히 100만큼 주어 사라지게 오프셋을 설정해주죠.
그리고 마지막으로 각 상/하단 뷰의 배경색을 이제 어느 한 선택지가 선택되었다면 clear하게 하여 아래 깔린 배경 색으로 보여지도록 하고, 만약 아무것도 선택되지 않은 기본 상태라면 각각의 배경색을 가지도록 합니다.
이러면 우리가 초반에 봤던 시뮬레이터 영상처럼 인터랙티브 애니메이션이 구현됨을 확인할 수 있어요!
아주 간단하죠?ㅎㅎ
마무리
그냥 이번 포스팅은 정말 제가 해보고 싶었던거를 아무말처럼 코드와 함께 소개해봤어요 😃
물론 이건 인터랙티브 애니메이션을 구현하는 과정이라는 큰 틀에서 여러분들도 다양하게 적용해보면 도움이 될거라 생각합니다.
위 코드 예시는 아래 깃헙 링크로 오픈되어 있으니 자유롭게 보고 돌려보셔도 됩니다 🏃🏻
'SwiftUI' 카테고리의 다른 글
SwiftUI로 테두리 오버레이 효과 만들기 (feat. ZStack) (5) 2024.11.14 SwiftUI의 Text에 stroke 적용하기 (2) 2024.11.07 SwiftUI - Color 혼합하기 (0) 2024.10.28 SwiftUI - ContainerValueKey (3) 2024.10.24 SwiftUI - NavigationTransition (6) 2024.10.21