【Swift UI】Viewを任意の形にトリミングする方法!clipShape/mask/trim(from:,to:)

【Swift UI】Viewを任意の形にトリミングする方法!clipShape/mask/trim(from:,to:)

この記事からわかること

  • Swift UIView任意の形トリミングする方法
  • clipShape/mask/trim(from:,to:)使い方

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

Swift UIでViewを任意の形にトリミングする方法

Swift UIでViewを任意の形にトリミングする(繰り抜く)方法は色々あるかと思います。今回は以下の3つの違いと使い方をまとめていきます。

  1. clipShape(_:style:)
  2. mask(alignment:_:)
  3. trim(from:,to:)

clipShape(_:style:)

公式リファレンス:clipShape(_:style:)

nonisolated
func clipShape<S>(
    _ shape: S,
    style: FillStyle = FillStyle()
) -> some View where S : Shape

clipShapeView型が持つメソッドで呼び出し元のビューを指定したShape型に繰り抜くことができるモディファイアです。Shape型で渡せば良いのでRoundedRectangleCircleなどの図形Viewや独自に定義したShapeプロトコルに準拠した形であればトリンミングすることが可能になります。

角丸四角形

Image("night_scenery")
    .frame(width: 400, height: 500)
    .clipShape(RoundedRectangle(cornerRadius: 20))
【Swift UI】Viewを任意の形にトリミングする方法!clipShape/mask/trim(from:,to:)

円形

Image("night_scenery")
    .frame(width: 400, height: 500)
    .clipShape(Circle())
【Swift UI】Viewを任意の形にトリミングする方法!clipShape/mask/trim(from:,to:)

mask(alignment:_:)

公式リファレンス:mask(alignment:_:)

nonisolated
func mask<Mask>(
    alignment: Alignment = .center,
    @ViewBuilder _ mask: () -> Mask
) -> some View where Mask : View

maskView型が持つメソッドで呼び出し元のビューを指定したビューの形状と透明度を元に型に繰り抜くことができるモディファイアです。View型で指定できるのでTextを渡せば文字形状で繰り抜くことができるようになります。

Image("night_scenery")
  .frame(width: 400, height: 500)
  .mask {
      Text("GOOG NIGHT")
          .fontWeight(.bold)
          .font(.system(size: 70))
  }
【Swift UI】Viewを任意の形にトリミングする方法!clipShape/mask/trim(from:,to:)

trim(from:,to:)

公式リファレンス:trim(from:,to:)

nonisolated
func trim(
    from startFraction: CGFloat = 0,
    to endFraction: CGFloat = 1
) -> some Shape

trimShape型が持つメソッドでで呼び出し元のShapeの開始と終了を指定することで部分的に描画するためのモディファイアです。clipShapemaskのように指定した形に繰り抜く役割ではなく、既存の形の描画範囲を制御するためのものになります。

円形

Circle()
      .trim(from: 0, to: 0.5)
【Swift UI】Viewを任意の形にトリミングする方法!clipShape/mask/trim(from:,to:)

矩形

Rectangle()
      .trim(from: 0, to: 0.5)
【Swift UI】Viewを任意の形にトリミングする方法!clipShape/mask/trim(from:,to:)

矩形(Rectangle)を半分(0.5)でトリミングしてみると上記のような斜めにカットされた図形になります。これは単純に長方形の半分が表示されるわけではなくShape内でPath構造体を使用して描いている工程の半分が指定されているようです。

Rectangleの中身が以下のように定義されているとイメージするとわかりやすいです。(※実装の定義を確認しておりません)

struct Rectangle : Shape {
    
    public func path(in rect: CGRect) -> Path {
        var path = Path()
        path.addLines(
            [   // 矩形を描画する 
                CGPoint(x: 50, y: 50),    // 左上角
                CGPoint(x: 100, y: 50),   // 右上角
                CGPoint(x: 100, y: 100),  // 右下角 // 半分なのでここまで描画されている
                CGPoint(x: 50, y: 100),   // 左下角
                CGPoint(x: 50, y: 50),    // 左上角へ戻る 
            ]
        )
        return path
    }
}

アニメーションで描く

Shape型はAnimatableを継承しているためアニメーションを実装することが可能です。

public protocol Shape : Animatable, View {
    func path(in rect: CGRect) -> Path
}

trim(from:,to:)に渡す値を変化させることで「チェックマークを書いている」ような実装をしてみたいと思います。用意するのは描画対象のCheckMarkShape型とアニメーション機能を持たせたAnimatedCheckMarkViewです。

/// チェックマークをアニメーションで描くビュー
struct AnimatedCheckMarkView: View {
    @Binding var drawProgress: CGFloat
    public var size: CGFloat = 30
    public var lineWidth: CGFloat = 4

    var body: some View {
        CheckMarkShape()
            .trim(from: 0, to: drawProgress)
            .stroke(Color.green, lineWidth: lineWidth)
            .frame(width: size, height: size)
            .animation(.easeIn(duration: 0.4), value: drawProgress)
    }
}
/// チェックマーク型
struct CheckMarkShape: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        
        let startPoint = CGPoint(x: rect.width * 0.2, y: rect.height * 0.5)
        let midPoint = CGPoint(x: rect.width * 0.4, y: rect.height * 0.7)
        let endPoint = CGPoint(x: rect.width * 0.8, y: rect.height * 0.3)

        path.move(to: startPoint)
        path.addLine(to: midPoint)
        path.addLine(to: endPoint)

        return path
    }
}

あとはボタンなどからdrawProgressの値を変化させればチェックマークが打たれたようなアニメーションを実装することができました。

struct ContentView: View {
    @State private var drawProgress: CGFloat = 0.0
    var body: some View {
        VStack {
            
            AnimatedCheckMarkView(drawProgress: $drawProgress)
            
            Button {
                drawProgress = drawProgress == 1.0 ? 0.0 : 1.0
            } label: {
                Text("Draw Chack Mark")
            }
        }.padding()
    }
}
【Swift UI】Viewを任意の形にトリミングする方法!clipShape/mask/trim(from:,to:)

今回使用している画像は無料画像サイトpixabayから利用しています。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index