【Swift UI】Viewを任意の形にトリミングする方法!clipShape/mask/trim(from:,to:)
この記事からわかること
- Swift UIでViewを任意の形にトリミングする方法
- clipShape/mask/trim(from:,to:)の使い方
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Xcode:16.0
- iOS:18.0
- Swift:5.9
- macOS:Sonoma 14.6.1
Swift UIでViewを任意の形にトリミングする方法
Swift UIでViewを任意の形にトリミングする(繰り抜く)方法は色々あるかと思います。今回は以下の3つの違いと使い方をまとめていきます。
- clipShape(_:style:)
- mask(alignment:_:)
- trim(from:,to:)
clipShape(_:style:)
nonisolated
func clipShape<S>(
_ shape: S,
style: FillStyle = FillStyle()
) -> some View where S : Shape
clipShape
はView
型が持つメソッドで呼び出し元のビューを指定したShape
型に繰り抜くことができるモディファイアです。Shape
型で渡せば良いのでRoundedRectangle
やCircle
などの図形Viewや独自に定義したShape
プロトコルに準拠した形であればトリンミングすることが可能になります。
角丸四角形
Image("night_scenery")
.frame(width: 400, height: 500)
.clipShape(RoundedRectangle(cornerRadius: 20))
円形
Image("night_scenery")
.frame(width: 400, height: 500)
.clipShape(Circle())
mask(alignment:_:)
nonisolated
func mask<Mask>(
alignment: Alignment = .center,
@ViewBuilder _ mask: () -> Mask
) -> some View where Mask : View
mask
もView
型が持つメソッドで呼び出し元のビューを指定したビューの形状と透明度を元に型に繰り抜くことができるモディファイアです。View
型で指定できるのでText
を渡せば文字形状で繰り抜くことができるようになります。
Image("night_scenery")
.frame(width: 400, height: 500)
.mask {
Text("GOOG NIGHT")
.fontWeight(.bold)
.font(.system(size: 70))
}
trim(from:,to:)
nonisolated
func trim(
from startFraction: CGFloat = 0,
to endFraction: CGFloat = 1
) -> some Shape
trim
はShape
型が持つメソッドでで呼び出し元のShapeの開始と終了を指定することで部分的に描画するためのモディファイアです。clipShape
やmask
のように指定した形に繰り抜く役割ではなく、既存の形の描画範囲を制御するためのものになります。
円形
Circle()
.trim(from: 0, to: 0.5)
矩形
Rectangle()
.trim(from: 0, to: 0.5)
矩形(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()
}
}
今回使用している画像は無料画像サイトpixabayから利用しています。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。