[Swift] SwiftUI로 화면을 자르십시오.

전치



유저에게 힌트를 내는 화면이 많은 앱으로 구현되고 있을까 생각합니다.



이 화면은 실제로 SwiftUI로 만들어집니다.
그렇다면 어떻게 했는지 살펴 보겠습니다.

구현 방법



2 패턴 소개하고 싶습니다.

1. UIBezierPath on SwiftUI로 구현


UIKit 의 경우는 UIBezierPath 를 사용한 구현 방법이 있었습니다.
다음은 Objective-C 코드이므로 낡습니다만, 비교적 알기 쉬운 코드 예입니다.
UIBezierPath 로 「화면 전체」와 「자르는 부분」을 겹치는 것으로, 겹친 부분만 투과할 수 있다고 하는 방법입니다. 이것을 SwiftUIView에 적응하기 만하면됩니다.

이번에는 아래와 같이 조금 간단하게 한 화면에서 소개하겠습니다.


빼기 전
빼낸 후






화면 구성



화면 자체는, 이하의 3개를 조합한 매우 심플한 구성으로 되어 있습니다.


① 경고 화면
②검은 필터 화면
③ 투명한 구멍 화면







① 경고 화면


struct TutorialAlertView: View {

    var handler: (() -> Void)?

    var body: some View {
        VStack {
            Text("Title")
                .padding(EdgeInsets(top: 8, leading: 0, bottom: 0, trailing: 0))

            Text("Description")
                .padding(EdgeInsets(top: 8, leading: 0, bottom: 0, trailing: 0))

            Button(action: { handler?() }, label: {
                Text("OK")
                    .frame(width: 252, height: 44)
                    .foregroundColor(.white)
                    .background(Color.red)
                    .cornerRadius(4)
                    .padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 0))
            })
            .contentShape(Rectangle())
        }
        .frame(maxWidth: 300)
        .background(Color.white)
        .cornerRadius(4)
    }
}

② 검은색 필터 화면


struct TutorialFilterView: View {

    var body: some View {
        Color.black
            .opacity(0.7)
            .mask(TutorialHoleView())
            .edgesIgnoringSafeArea(.all)
    }
}
mask()를 사용하여 필터 부분을 잘라냅니다.

③ 투명한 구멍 화면


struct TutorialHoleView: View {

    var body: some View {
        // 穴
        let holePath = UIBezierPath(roundedRect: CGRect(
            x: 8,
            y: 60,
            width: 100,
            height: 80
        ), cornerRadius: 24)

        // 全体
        var shape = Path(CGRect(
            origin: .zero,
            size: UIScreen.main.bounds.size
        ))
        shape.addPath(Path(holePath.cgPath)) // Pathに変換
        return shape.fill(style: FillStyle(eoFill: true))
    }
}
SwiftUI 에서는 Path 라고 하는 것이 있어, UIBezierPath 로부터 변환하는 것으로 취급할 수가 있습니다. 변환 자체는 .cgPath를 참조하는 것만으로 가능하므로 매우 간단합니다. FillStyle 가 없으면 투과 부분이 반전되어 버리므로 붙여 주십시오.

①~③을 조합한 화면



이것을 ZStack 로 겹쳐 조합하면 완성됩니다.


화면
중첩





struct TutorialView: View {

    var body: some View {
        ZStack {
            TutorialFilterView()
            TutorialAlertView { /* do some button action */ }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

이번은 예이므로 frame 를 고정으로 하고 있습니다만, 위치를 조정하는 경우는 전의 화면으로부터 건네주어 가야 합니다.

2. SwiftUI에서만 구현



방금 구현한 UIBezierPath 로 구현한 부분을 SwiftUI 의 Shape 를 사용해 구현합니다.

기본적으로는 거의 같고, 「③ 투명한 구멍의 화면」의 코드로 작성한 TutorialHoleView 부분을, 이하와 같이 Shape (Rectangle)로 합니다.

example code


struct TutorialHoleView: View {

    var body: some View {
        Rectangle()
            .cornerRadius(24)
            .frame(width: 100, height: 80)
            .position(x: 60, y: 150)
            .background(Color.white)
            .compositingGroup() // ※1
            .luminanceToAlpha() // ※2
    }
}
Shape를 사용하는 경우 다음 두 가지를주의 사항으로 설정해야합니다.
  • compositingGroup() : ZStack 내용 그룹화
  • luminanceToAlpha() : 어두운 영역은 투명하고 밝은 영역은 불투명 한 검은 색으로 만듭니다

  • 이것을 설정하면 펀칭을 반영할 수 있습니다.

    또한 Rectangle를 사용하고 있지만 Shape이면 좋기 때문에,
  • RotatedShape
  • RoundedRectangle
  • Circle

  • 등 다양한 도형을 사용하여 빠져 나올 수 있습니다.

    결합된 화면




    화면
    중첩






    이와 같이 SwiftUI 가 준비한 도형을 이용하는 것으로, 심플한 구현으로 할 수 있습니다.

    구분



    간단하게 빼내기의 구현이면, SwiftUIShape 를 사용한 구현이 편하게 구현 가능합니다. 단, 별 모양이나 하트 등, 어려운 도형을 빼내고 싶은 경우는, Path or UIBezierPath 로 도형을 작성하게 됩니다.

    동작하는 리포지토리를 두어 두므로, 여러가지 시험해 보면 재미있을까 생각합니다.

    기타


  • 「1. UIBezierPath on SwiftUI로 구현」의 구현으로 참고가 된 것


  • 「2. SwiftUI만으로 구현」의 구현으로 참고가 된 것


  • UIBezierPath 의 방법을 소개했습니다만, Path 로 도형을 만드는 방법도 게재해 둡니다
  • 좋은 웹페이지 즐겨찾기