기어가더라도 제대로
[SwiftUI-기초] Animation의 View적인 특성(+ Drag, transition) 본문
SwiftUI - 기초
[SwiftUI-기초] Animation의 View적인 특성(+ Drag, transition)
Damagucci-juice 2022. 10. 30. 09:31- animation도 background, padding, 과 마찬가지로 some View 를 반환한다.
- 여기서 도출되는 결론이 두가지 있다.
- 애니메이션을 적용하는 순서가 중요하다.
- 여러개의 애니메이션을 중첩해서 사용할 수 있다.
위의 두 가지 결론을 쫒아가보는 포스팅이 될 것이다.
View 적인 특성에 대해서 알고 싶으시다면 다음 포스팅을 참조하시라.
2022.10.14 - [Swift - 기초] - [SwiftUI-기초] Modifier 적용 순서가 중요한 이유
애니메이션의 적용 순서가 중요한 이유
결론부터 말하자면, .animation() modifier 앞에 적용된 modifier들만 애니메이션 처리가 된다.
.animation 뒤로 적용된 modifier의 경우에는 애니메이션 처리가 되지 않는다.
@State private var enabled = false
var body: some View {
Button("Tap Me") {
enabled.toggle()
}
.frame(width: 200, height: 200)
.background(enabled ? .blue : .red)
.animation(.default, value: enabled)
.foregroundColor(.white)
}
- 위의 코드는 배경색이 state 의 변화에 따라 파랑색에서 빨간색으로 변화하는 변화 자체가 애니메이션화 된다.
- 하지만 .animation 과 .background 의 순서가 바뀌면 애니메이션이 적용이 안된채로 바로 변화하게 된다.
- 배경색 변화가 애니메이션 처리 된 모습
- 배경색 변화가 애니메이션 처리 안된 모습
애니메이션의 선언 위에 있는 코드들만이 애니메이션 변화의 효과를 적용받게 된다.
여러 .animation() 의 중첩
Button("Tap Me") {
enabled.toggle()
}
.frame(width: 200, height: 200)
.background(enabled ? .blue : .red)
.animation(.default, value: enabled)
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: enabled ? 60 : 0))
.animation(.interpolatingSpring(stiffness: 10, damping: 1), value: enabled)
- 애니메이션이 배경색을 적용한 애니메이션 하나와, 도형의 모양에 영향을 주는 애니메이션이 두개가 있다.
- 다음과 같이 적용이 됩니다.
- 용수철 효과가 적용된 모습
- 짤방의 화면 한계 때문에 덜 보이는데, 빨강, 파랑 색 전환도 애니메이션을 먹는 모습입니다. 도형의 모양도 당연히 애니메이션을 받고요.
- 만약에 색전환은 애니메이션 적용을 안하고, 도형의 모양만 애니메이션 적용을 하고 싶다면 어떻게 해야할까요?
- 첫번째 선언된 애니메이션을 빼야할까요?
- 그렇지 않습니다.
- 위의 애니메이션을 빼면, 최종 애니메이션에 의해서 어차피 적용이 될거에요.
- 정답은 .default 대신 nil 를 넣는 것입니다.
Button("Tap Me") {
enabled.toggle()
}
.frame(width: 200, height: 200)
.background(enabled ? .blue : .red)
.animation(nil, value: enabled)
.foregroundColor(.white)
.clipShape(RoundedRectangle(cornerRadius: enabled ? 60 : 0))
.animation(.interpolatingSpring(stiffness: 10, damping: 1), value: enabled)
- 이렇게 하면 첫번째 애니메이션 modifier 위로는 애니메이션을 적용하지 않는다라고 선언하는 것과 같습니다.
- 색상이 애니메이션 없이 전환되는 모습
- 도형 변화는 애니메이션이 적용 되었다.
View 에 드래그 제스쳐 적용하기
- 3가지를 다루면 위에 내용을 구현할 수 있습니다.
- 1. drag 를 할 때 위치를 저장할 State 를 선언한다.
- 2. .offset() 으로 원래에 얼마만큼 떨어졌는지를 붙여준다. (마침, CGSize 값도 파라미터로 받습니다.)
- 3. drag 제스쳐를 붙여준다.(이동중, 끝을 알리는 클로저를 선언한다.)
스텝 바이 스텝으로, 사각형 하나를 움직이는 로직을 이해해보죠.
struct ContentView: View {
@State private var dragAmount = CGSize.zero
var body: some View {
LinearGradient(gradient: Gradient(colors: [.yellow, .red]), startPoint: .topLeading, endPoint: .bottomTrailing)
.frame(width: 300, height: 200)
.clipShape(RoundedRectangle(cornerRadius: 10))
.offset(dragAmount)
.gesture(
DragGesture()
.onChanged { dragAmount = $0.translation }
.onEnded { _ in dragAmount = .zero }
)
.animation(.spring(), value: dragAmount)
}
}
- 300 * 200 정도의 카드를 움직이는 로직입니다.
- .offset(dragAmount): 원래 위치와 얼마만큼 떨어져있는지를 알려주는 modifier 입니다.
- .gesture(): 안에 tap 제스쳐도 넣을 수 있고, 이번에는 drag 제스쳐를 넣었습니다.
- .onChage { } 를 보면, 현재 움직이는 위치를 $0 이라고 한다면, 그것의 위치를 변환해서 dragAmount 에 넣는 것입니다.
- 이대로면 당겨오겠죠?
- .onEnded { } 이것은 드래그하는 손가락을 때었을 때 나타나는 모습입니다. 다시 원위치로 돌아가는 모습입니다.
- .animation() 매끄러운 애니메이션 변화를 주었고요.
- 근데 한가지 문제가 있습니다. 드래그 시작부터 애니메이션이 걸려서 좀 실제 손가락과 딜레이가 느껴집니다.
- 움직임 딜레이는 없되, 되돌아가는 모습은 애니메이션을 넣어주는 방식으로 해결 가능하겠네요 .
.gesture(
DragGesture()
.onChanged { dragAmount = $0.translation }
.onEnded { _ in
withAnimation(.spring()) {
dragAmount = .zero
}
}
)
- 마지막에 .animation 은 지워야합니다.
첫 번째 움짤의 코드는 글자의 배열만큼 옮기는 작업이 추가된 것입니다.
전체 코드입니다.
struct ContentView: View {
let letters = Array("Hello SwiftUI")
@State private var enabled = false
@State private var dragAmount = CGSize.zero
var body: some View {
HStack(spacing: 0) {
ForEach(0..<letters.count, id: \.self) { num in
Text(String(letters[num]))
.padding(5)
.font(.title)
.background(enabled ? .blue : .red)
.offset(dragAmount)
.animation(.default.delay(Double(num) / 20), value: dragAmount)
}
}
.gesture(
DragGesture()
.onChanged { dragAmount = $0.translation }
.onEnded { _ in
dragAmount = .zero
enabled.toggle()
}
)
}
}
뷰 화면 전환 - transition
- if 문을 통해서 어떤 뷰를 넣을지 말지를 결정한 적이 있다.
- 어떤 뷰가 생길 때 자신의 화면 전환을 어떻게 담당할지 정하는 방법이 있는데 .trasition() 이라고 한다.
- 다음은 버튼을 눌렀을 때 빨간색 사각형을 띄우는 코드이다.
struct ContentView: View {
@State private var isShowingRed = false
var body: some View {
VStack {
Button("Tap Me") {
isShowingRed.toggle()
}
if isShowingRed {
Rectangle()
.fill(.red)
.frame(width: 200, height: 200)
}
}
}
}
- 이 코드를 애니메이션을 넣는 방법은 다음과 같다.
struct ContentView: View {
@State private var isShowingRed = false
var body: some View {
VStack {
Button("Tap Me") {
withAnimation {
isShowingRed.toggle()
}
}
if isShowingRed {
Rectangle()
.fill(.red)
.frame(width: 200, height: 200)
.transition(.scale)
}
}
}
}
- 또한 .transition 에 insert 하는 화면하고 removal 하는 화면을 나눠서 편집할 수가 잇는데, 다음과 같다.
.transition(.asymmetric(insertion: .scale, removal: .opacity))
- 스케일을 키우면서 등장하고, 흐리게 페이드 아웃하며 사라진다.
'SwiftUI - 기초' 카테고리의 다른 글
[SwiftUI-기초] @State가 쓸모 없어지는 지점에 대하여(class, struct) (0) | 2022.11.01 |
---|---|
[SwiftUI-기초] custom transition (0) | 2022.10.31 |
[SwiftUI-기초] Animation 기본 (0) | 2022.10.28 |
[SwiftUI-기초] View 생명주기, View 등장 시 실행 (0) | 2022.10.26 |
[SwiftUI-기초] String 과 작업하기 (0) | 2022.10.24 |
Comments