안녕하세요.
SwiftUI에서 Shape에 애니메이션이 적용 안 되는 경우가 있더라구요.
그래서 이번 글에서는 animatableData를 사용해서 Shape에 애니메이션을 적용하는 방법에 대해 알아볼게요.
우선 팔각형 형태의 Octagon이라는 Shape를 만들게요.
struct Octagon: Shape {
var inset: CGFloat
func path(in rect: CGRect) -> Path {
return Path { path in
path.move(to: CGPoint(x: inset, y: 0))
path.addLines([
CGPoint(x: 0, y: inset),
CGPoint(x: 0, y: rect.height - inset),
CGPoint(x: inset, y: rect.height),
CGPoint(x: rect.width - inset, y: rect.height),
CGPoint(x: rect.width, y: rect.height - inset),
CGPoint(x: rect.width, y: inset),
CGPoint(x: rect.width - inset, y: 0),
CGPoint(x: inset, y: 0),
])
}
}
}
그 다음에 Octagon을 터치하면 inset에 랜덤한 값을 주도록 해볼게요.
struct ContentView: View {
@State var inset: CGFloat = 50
var body: some View {
Octagon(inset: inset)
.frame(width: 200, height: 200)
.foregroundColor(.blue)
.onTapGesture {
inset = CGFloat.random(in: 10...90)
}
}
}
모양이 변하긴 하지만, 애니메이션이 적용 안되었네요.
withAnimation 함수를 사용해볼게요.
struct ContentView: View {
@State var inset: CGFloat = 50
var body: some View {
Octagon(inset: inset)
.frame(width: 200, height: 200)
.foregroundColor(.blue)
.onTapGesture {
withAnimation { ✅
inset = CGFloat.random(in: 10...90)
}
}
}
}
엥...?? 그래도 애니메이션이 적용 안되었습니다...
Shape는 애니메이션이 적용 안되나?? 하는 생각에 터치할 때마다 랜덤하게 scale 되는 코드를 추가해봤어요.
struct ContentView: View {
@State var inset: CGFloat = 50
@State var scale: CGFloat = 1 ✅
var body: some View {
Octagon(inset: inset)
.frame(width: 200, height: 200)
.foregroundColor(.blue)
.scaleEffect(scale) ✅
.onTapGesture {
withAnimation {
inset = CGFloat.random(in: 10...90)
scale = CGFloat.random(in: 0.5...1.5) ✅
}
}
}
}
scale 효과는 애니메이션이 적용되었는데, inset 변경은 애니메이션이 적용되지 않는 것을 볼 수 있습니다.
왜 scale이 바뀔 때는 애니메이션이 적용되고, inset이 변경될 때는 애니메이션이 적용 안될까요??
그 이유는 SwiftUI 애니메이션 효과가 적용되는 조건을 봐야 해요.
SwiftUI의 애니메이션 효과는 뷰의 상태가 변경되었을 때, 상태 A -> 상태 B로 점차 바뀌면서 애니메이션 효과를 주게 됩니다.
만약 뷰의 scale이 1.0 -> 2.0으로 변경되었다면, 1.0에서 2.0으로 바로 바뀌는 것이 아니라, scale = 1.0, 1.1, 1.2, ..., 2.0이 적용되면서 애니메이션 효과를 주는 것이죠.
그런데 위의 inset은 뷰의 상태는 아니기 때문에 애니메이션 효과가 적용되지 않는 것이죠..ㅠㅠ
그럼 뷰의 상태가 아닌 특정 값이 변경되었을 때 애니메이션 효과를 주려면???
이때 animatableData가 사용됩니다.
애니메이션 효과를 주고 싶은 뷰 안에 animatableData 프로퍼티와 getter, setter를 구현해주면 됩니다ㅎㅎ
위에서 구현한 Octagon 뷰에 animatableData를 구현해볼게요.
struct Octagon: Shape {
var inset: CGFloat
var animatableData: CGFloat { ✅
get { inset }
set { inset = newValue }
}
func path(in rect: CGRect) -> Path {
return Path { path in
path.move(to: CGPoint(x: inset, y: 0))
path.addLines([
CGPoint(x: 0, y: inset),
CGPoint(x: 0, y: rect.height - inset),
CGPoint(x: inset, y: rect.height),
CGPoint(x: rect.width - inset, y: rect.height),
CGPoint(x: rect.width, y: rect.height - inset),
CGPoint(x: rect.width, y: inset),
CGPoint(x: rect.width - inset, y: 0),
CGPoint(x: inset, y: 0),
])
}
}
}
Octagon에 애니메이션 적용할 거임?? 어떤 데이터가 변경되었을 때 애니메이션을 적용해줄까?? 의 의미라고 보시면 됩니다.
애니메이션이 진짜 잘 적용되는지 확인해볼까요??
👍 👍 👍
## 참고
- https://www.hackingwithswift.com/books/ios-swiftui/animating-simple-shapes-with-animatabledata
이번 글은 여기서 마무리.
'SwiftUI' 카테고리의 다른 글
cornerRadius (0) | 2022.03.13 |
---|---|
SwiftUI에서 testable 한 코드 만들기 (0) | 2022.03.05 |
renderingMode(_:) (0) | 2022.03.01 |
AlignmentGuide (0) | 2022.02.27 |
스크린샷 주의 팝업, 화면 녹화 방지 (3) | 2022.02.25 |