【SwiftUI】リバースマスクを実装し、Viewを切り抜く方法
以前、UIView
で切り抜く方法は紹介したのですが、せっかくなのでSwitUIのView
でも切り抜く方法を調べてみました。
環境
- Xcode 14
Viewをマスク
図形で切り抜き
SwiftUIでクリッピングマスクを行うのは非常に簡単です。
図形で切り取りを行いたい場合は、.clipShape
を使用します
Image("img_mask") .resizable() .frame(width: 300, height: 300) .clipShape(Circle())
Viewで切り抜き
Shape
型だけではなく、特定のView
で切り抜くことも出来ます。
Image("img_mask") .resizable() .frame(width: 300, height: 300) .mask { Circle() }
Viewの中を切り抜く
SwiftUIにはリバースマスクのAPIがまだ無い為、View
の中を切り抜くにはまだ一手間必要です。
マスクの原理
簡単な説明になりますが、今回のCircle
で切り抜く例で言いますと、.mask
に記述したView
と重なっている箇所だけが描画されるという動きになっています。
重なっている箇所 | mask後 |
---|---|
なので、リバースマスクを表現する為には、切り抜きたい箇所が切り抜かれたView
が必要です。
切り抜かれたViewを作成
.blendMode(.destinationOut)
を使用して合成した切り抜かれたView
を作成します。
.blendMode
は、View
とその重なり合うView
とを合成するためのモードを設定します。
.destinationOut
を設定すると、重なり合っている部分だけを切り抜き、切り抜かれた状態のものが残ります。また、compositingGroup()
を使用して合成効果を有効にする為にラップしています。
Zstackを使用した例
ZStack { Rectangle() Circle() .blendMode(.destinationOut) } .compositingGroup() .background(.mint)
overlayを使用した例
Rectangle() .fill(.black) .overlay() { Circle() .blendMode(.destinationOut) } .compositingGroup() .background(.mint)
プレビュー
円の形が切り抜かれたView
が完成しました。しっかり背景の色が切り抜かれた箇所から確認が出来ます。
切り抜かれたViewでマスクする
.mask
の中に作成した切り抜かれたView
を渡してみましょう。
Image("img_mask") .resizable() .frame(width: 300, height: 300) .mask { // 元々切り抜かれたView Rectangle() .overlay() { Circle() .blendMode(.destinationOut) } .compositingGroup() }
プレビュー
View
の中が円形に切り抜かれて出力出来ました!
Extensionを作成
汎用的に使用する為のExtensionを作成しました。
extension View { func reverseMask<Mask: View>(alignment: Alignment = .center, @ViewBuilder _ mask: () -> Mask) -> some View { self.mask { Rectangle() .overlay(alignment: alignment) { mask() .blendMode(.destinationOut) } // .compositingGroup() 無くても機能する } } }
使用例
Image("img_mask") .resizable() .frame(width: 300, height: 300) .reverseMask { Image(systemName: "applelogo") .font(.system(size: 100)) }
プレビュー
おしゃれな感じに仕上がりました!
おわりに
SwiftUIでもView
を切り抜く表現をすることが出来ました!今後のSwiftUIのバージョンアップで手軽に実装出来るようになるとさらに良いですね!
この切り抜きを活用した何かを作りたくなってきたので、また試したら記事にしてみようと思います。
素敵な切り抜きライフをお過ごしください?