How to create custom Transitions in SwiftUI | Advanced Learning #3
Transition
커스텀AnyTransition
익스텐션에 추가static
변수로, 파라미터가 필요하다면 함수 선언 가능struct RotateViewModifier: ViewModifier {
let angleDegree: Double
init(angleDegree: Double = 40.0) {
self.angleDegree = angleDegree
}
func body(content: Content) -> some View {
content
.rotationEffect(Angle(degrees: angleDegree))
.offset(x: angleDegree != 0 ? UIScreen.main.bounds.width : 0,
y: angleDegree != 0 ? UIScreen.main.bounds.height : 0)
}
}
extension AnyTransition {
static var rotating: AnyTransition {
modifier(
active: RotateViewModifier(angleDegree: 180),
identity: RotateViewModifier(angleDegree: 0))
}
static func rotating(angleDegree: Double) -> AnyTransition {
modifier(
active: RotateViewModifier(angleDegree: angleDegree),
identity: RotateViewModifier(angleDegree: 0))
}
static var rotationOn: AnyTransition {
asymmetric(
insertion: .rotating,
removal: .move(edge: .leading))
}
}
AnyTransition
은 활성화 상태와 그렇지 않은 상태를 구별하기 위해 뷰 모디파이어를 두 개 받는다. → 회전 상태를 보여주기 위한 RotationModifier
의 경우 서로 다른 각도를 줌으로써 '회전하는 것처럼' 보이게 만들 수 있다.import SwiftUI
struct RotateViewModifier: ViewModifier {
let angleDegree: Double
init(angleDegree: Double = 40.0) {
self.angleDegree = angleDegree
}
func body(content: Content) -> some View {
content
.rotationEffect(Angle(degrees: angleDegree))
.offset(x: angleDegree != 0 ? UIScreen.main.bounds.width : 0,
y: angleDegree != 0 ? UIScreen.main.bounds.height : 0)
}
}
extension AnyTransition {
static var rotating: AnyTransition {
modifier(
active: RotateViewModifier(angleDegree: 180),
identity: RotateViewModifier(angleDegree: 0))
}
static func rotating(angleDegree: Double) -> AnyTransition {
modifier(
active: RotateViewModifier(angleDegree: angleDegree),
identity: RotateViewModifier(angleDegree: 0))
}
static var rotationOn: AnyTransition {
asymmetric(
insertion: .rotating,
removal: .move(edge: .leading))
}
}
struct TransitionsBootCamp: View {
@State private var transitionType: Int = 0
@State private var showRectangle: Bool = false
var body: some View {
VStack {
HStack(spacing: 20) {
Button {
transitionType = 0
} label: {
Text("First Transition")
.font(.headline)
.withDefaultButtonFormmating(.pink)
}
.withPressableStyle(0.8)
Button {
transitionType = 1
} label: {
Text("Second Transition")
.font(.headline)
.withDefaultButtonFormmating(.indigo)
}
.withPressableStyle(0.8)
}
.padding(.horizontal, 30)
Spacer()
if showRectangle {
RoundedRectangle(cornerRadius: 25)
.frame(width: 250, height: 350)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.transition(transitionType == 0 ? .rotating(angleDegree: 1080) : .rotationOn)
}
Spacer()
Text("Click Me")
.withDefaultButtonFormmating()
.padding(.horizontal, 40)
.onTapGesture {
withAnimation(.easeInOut) {
showRectangle.toggle()
}
}
}
}
}
asymmetric
를 통해 들어오는 방향과 나가는 방향이 일치하지 않을 수 있도록 구현 가능