반응형

 

Hacking with Swift 사이트의 강좌 번역본입니다.

 

[원문 : https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-a-custom-transition]

 

How to create a custom transition

 

SwiftUI는 다양한 전환기능이 내장되어 있고, 원하는 경우, 완전한 사용자정의 전환을 작성하는 것도 가능합니다. 

 

그 처리는 3 단계를 거칩니다.

  1. 어떤 상태에 전환을 표현하는 ViewModifier를 만듭니다.
  2. 활성(active)과 식별(identity) 상태에 대한 뷰 modifer에 사용하는 AnyTransition 확장을 만듭니다.
  3. transition() modifer를 사용해서 뷰에 전환을 적용합니다.

예를들어, 키노트(Keynote)에서 Iris 애니메이션을 흉내낼수 있는 도형을 작성하고 뷰 modifier를 결합할 수 있습니다 - 새로운 슬라이드가 위로 커지는 원 모양이 나타나게 할 것이며, 오래된 Looney Tunes 인스토 시퀀스와 약간 비슷합니다. 

 

이 작업을 위해서 약간의 애니메이션가능한 데이터에 따라 크기가 조절되는 사각형 내부에 원을 만드는 ScaledCircle 도형을 정의합니다.

struct ScaledCircle: Shape {
    var animatableData: CGFloat

    func path(in rect: CGRect) -> Path {
        let maximumCircleRadius = sqrt(rect.width * rect.width + rect.height * rect.height)
        let circleRadius = maximumCircleRadius * animatableData

        let x = rect.midX - circleRadius / 2
        let y = rect.midY - circleRadius / 2

        let circleRect = CGRect(x: x, y: y, width: circleRadius, height: circleRadius)

        return Circle().path(in: circleRect)
    }
}

 

보다시피, 사각형을 그리는 내부의 원의 크기를 제어하는 animatableData 프로퍼티를 노출합니다. 따라서 animatableData가 0일때 원은 보이지 않고, 1일때 원은 사각형에 가득찹니다.

 

이제 도형(이 경우에, 확대하는 원)을 다른 뷰를 잘라내는(clip) 도형으로 적용가능한 사용자정의 ViewModifier 구조체를 만들 수 있습니다.

struct ClipShapeModifier<T: Shape>: ViewModifier {
    let shape: T

    func body(content: Content) -> some View {
        content.clipShape(shape)
    }
}

 

다음으로, 더 쉽게 사용할 수 있도록 전환(transition)에서 해당 modifier로 감싸는 AnyTransition 확장을 만들 수 있습니다.

extension AnyTransition {
    static var iris: AnyTransition {
        .modifier(
            active: ClipShapeModifier(shape: ScaledCircle(animatableData: 0)),
            identity: ClipShapeModifier(shape: ScaledCircle(animatableData: 1))
        )
    }
}

 

마지막으로 해당 전환(transition)을 .transition(.iris)와 함께 사용할 수 있습니다. 예를들어, 파란색 사각형으로 ZStack을 만들고, Swiftch 버튼이 탭될때 빨간 사각형 안으로 미끄러집니다.

struct ContentView: View {
    @State private var isShowingRed = false

    var body: some View {
        NavigationView {
            ZStack {
                Rectangle()
                    .fill(Color.blue)
                    .frame(width: 300, height: 300)

                if isShowingRed {
                    Rectangle()
                        .fill(Color.red)
                        .frame(width: 300, height: 300)
                        .transition(.iris)
                        .zIndex(1)
                }
            }
            .navigationBarItems(trailing: Button("Switch") {
                withAnimation(.easeInOut) {
                    self.isShowingRed.toggle()
                }
            })
        }
    }
}

 

반응형
Posted by 까칠코더
,