100 days of swiftui: 33
https://www.hackingwithswift.com/100/swiftui/33
animation도 modifier이기 때문에 순서가 중요하다. 어떤 순서로 적용했는지, 어떤 것들을 적용했는지에 따라 달리 표현할 수 있다. 아래 예시는 색과 움직임에 각각 animation
을 설정했다.
Button("Tap here") {
animationValue.toggle()
}
.padding(50)
.background(animationValue ? .green : .blue)
// 1
.animation(.easeInOut(duration: 1).delay(1), value: animationValue)
.foregroundColor(.white)
.clipShape(.circle)
.rotation3DEffect(
.degrees(animationValue ? 360 : 0), axis: (x: 1.0, y: 1.0, z: 1.0))
// 2
.animation(.spring(duration: 1, bounce: 0.6), value: animationValue)
배경색이 변경되는 animation은 1번으로 delay: 1초로 인해 1초 뒤에 표현된다. 그와 별개로 rotate animation은 delay 없이 spring 형태로 표현된다. 이처럼 animation은 자신의 이전 animation modifier의 다음 코드부터 자신의 이전 코드까지 적용된다.
그래서 변경되는 값 이전에 animation modifier를 설정하면 먹히지 않는다.
예:
Button("Tap here") {
animationValue.toggle()
}
.padding(50)
.animation(.spring(duration: 1, bounce: 0.6), value: animationValue)
.animation(.easeInOut(duration: 1).delay(1), value: animationValue)
.background(animationValue ? .green : .blue)
.foregroundColor(.white)
.clipShape(.circle)
.rotation3DEffect(
.degrees(animationValue ? 360 : 0), axis: (x: 1.0, y: 1.0, z: 1.0))
drag를 활용하여 animation을 만들 수도 있다. 그리고 그 중 어디에 animation을 적용하냐에 따라 달리 표현할 수 있다.
아래 예시 중 왼쪽은 drag를 통해 dragAmount
의 변화에 animation을 적용시켰고, 오른쪽은 drag가 끝난 후(onEnded
) 원위치로 돌리는 것에 animation을 적용시켰다.
![]() | ![]() |
---|
RadialGradient(colors: [.cyan, .green, .yellow], center: .center, startRadius: 180, endRadius: 0)
.frame(width: 350, height: 350)
.clipShape(.rect(cornerRadius: 20))
.offset(dragAmount)
.gesture(
DragGesture()
.onChanged { dragAmount = $0.translation }
.onEnded { _ in
// drag가 끝난 후(`onEnded`) 원위치로 돌리는 것에 animation
withAnimation(.smooth) {
dragAmount = CGSize.zero
}
}
)
// dragAmount`의 변화에 animation
.animation(.smooth, value: dragAmount)
개인적으로 swiftui에서 drag를 어떻게 적용시킬 수 있을지 궁금했는데 알게 되어 기쁘다.
코드 파일
https://github.com/treesofgroo/Ios-Animations/commit/0d5b683a7099ae4b0807882724e5d70e914f81fd
아래는 조금 수정한 다른 예시다.
HStack(spacing: 0) {
ForEach(0..<letters.count, id: \.self) { num in
Text(String(letters[num]))
.font(.title)
.padding(10)
.frame(width: 40, height: 40)
.background(enabled ? .yellow : .green)
.clipShape(.circle)
.offset(dragAmount)
.animation(.linear.delay(Double(num) / 20), value: dragAmount)
}
}
.gesture(
DragGesture()
.onChanged { dragAmount = $0.translation }
.onEnded { _ in
dragAmount = .zero
enabled.toggle()
}
)
코드 파일
https://github.com/treesofgroo/Ios-Animations/commit/9913dd9d07471731511056878d0a844feb0c0805
특정 View가 나타나고 사라지는 것을 animation으로 나타낼 수 있는데, 이때 transition
을 통해 그 모양새를 설정할 수 있다. 그리고 asymmetric
을 통해 그 애니메이션의 초반과 후반을 다른 모양새로 설정할 수 있다.
VStack {
if isShowingElement {
Circle()
.frame(width: 200)
.transition(.asymmetric(insertion: .slide, removal: .push(from: .top)))
}
Button("Tap Here") {
withAnimation {
isShowingElement.toggle()
}
}
.padding()
.buttonStyle(.borderedProminent)
}
asymmetric(insertion:removal:)
https://developer.apple.com/documentation/swiftui/anytransition/asymmetric(insertion:removal:)
❔ 이유는 알 수 없지만 버튼을 반복해서 누르다 보면 설정한 transition animation과 달리 표현된다.
코드 파일
https://github.com/treesofgroo/Ios-Animations/commit/622ca43aca99af7a4c158f74c510cf24ec1773f9
ViewModifier를 활용해서 transition 동작을 직접 만들 수도 있다. 아래 예시는 rotationEffect
을 설정해서 z축 위치를 임으로 설정해서 요소를 회전시키면서 나타낸다.
struct CornerRotateModifier: ViewModifier {
let amount: Double
let anchor: UnitPoint
func body(content: Content) -> some View {
content
.rotationEffect(.degrees(amount), anchor: anchor)
}
}
extension AnyTransition {
static var pivot: AnyTransition {
.modifier(
active: CornerRotateModifier(amount: -90, anchor: .topLeading),
identity: CornerRotateModifier(amount: 0, anchor: .topLeading)
)
}
}
struct ContentView: View {
@State private var isShowingElement = false
var body: some View {
ZStack {
Circle()
.foregroundColor(.green)
.frame(width: 200)
if isShowingElement {
Circle()
.foregroundColor(.orange)
.frame(width: 200)
.transition(.pivot)
}
}
.onTapGesture {
withAnimation {
isShowingElement.toggle()
}
}
}
}
참고로 clipped()
를 추가해서 뒤의 Circle() 범위 내에서 표현하는 방법도 있다.
struct CornerRotateModifier: ViewModifier {
let amount: Double
let anchor: UnitPoint
func body(content: Content) -> some View {
content
.rotationEffect(.degrees(amount), anchor: anchor)
.clipped()
}
}
코드 파일
https://github.com/treesofgroo/Ios-Animations/commit/802f5cc8aa0b934a8a2da2385a8f4cf7f9261bca