内側が透明な要素を作って、それをクリックするための方法をメモ。
課題
SwiftUIでは、透明色を持つ部分は、その位置がその要素の描画領域内であっても、Gestureの対象になりません。
例えば、以下のような矩形です。
Rectangle()
.fill(Color.gray.opacity(0))
.border(Color.red, width: 3)
このような表示要素に対して、ドラッグ等のGestureを紐づけたとします。(おおよそ、先日のPostからコードを持ってきています)
Rectangle()
.fill(Color.gray.opacity(0))
.border(Color.red, width: 3)
.frame(width: frameSize.width, height: frameSize.height)
.offset(frameOffset)
.gesture(DragGesture()
.onChanged { gesture in
if self.isDragging == false {
self.dragStartPosition = CGPoint(x: self.frameOffset.width, y: self.frameOffset.height)
self.isDragging = true
}
self.frameOffset = CGSize(width: gesture.translation.width + self.dragStartPosition.x,
height: gesture.translation.height + self.dragStartPosition.y)
}
.onEnded { gesture in
self.isDragging = false
})
動かしてみるとわかるのですが、borderとして描画された細い線の部分をつかまないとドラッグできません。
これが、冒頭に書いた「SwiftUIでは、透明色を持つ部分は、その位置がその要素の描画領域内であっても、Gestureの対象になりません。」という意味です。
透明色を持つ部分に対してもGestureできるようにする方法
.contentShape という modifier が用意されています。この modifer で操作領域を定義することができます。極端な話、矩形/Rectangleにたいして、円形/Circleを操作領域として設定するということもできます。
例えば、今回は、少し外れてもドラッグできるようしたかったので、少し大きめのRectangleを指定するようにしました。
少し大きめ もしくは 少し小さめ を指定するためには、.inset という modifier が用意されています。
import SwiftUI
struct FrameView: View {
@State private var frameOffset: CGSize = CGSize.zero
@State private var frameSize: CGSize = CGSize(width: 300, height: 200)
@State var dragStartPosition: CGPoint = CGPoint.zero
@State var isDragging:Bool = false
var body: some View {
Rectangle()
.fill(Color.gray.opacity(0))
.border(Color.red, width: 3)
.frame(width: frameSize.width, height: frameSize.height)
.offset(frameOffset)
.contentShape(Rectangle().inset(by: -10)) // - ポイント
.gesture(DragGesture()
.onChanged { gesture in
if self.isDragging == false {
self.dragStartPosition = CGPoint(x: self.frameOffset.width, y: self.frameOffset.height)
self.isDragging = true
}
self.frameOffset = CGSize(width: gesture.translation.width + self.dragStartPosition.x,
height: gesture.translation.height + self.dragStartPosition.y)
}
.onEnded { gesture in
self.isDragging = false
})
}
}
struct FrameView_Previews: PreviewProvider {
static var previews: some View {
FrameView()
}
}
上記のコードでは、Rectangle の内側(と少し外側)まで、クリックしてドラッグすることができますが、
”ポイント”と書かれた行をコメントアウトすると、Rectangle の border (この例では赤い線)を きっちりとクリックしないとドラッグすることができません。
MacOS上で、マウスを使っても、詳細なクリックを期待することは難しいと思いますので、この .contentShape modifier を適切に使うと良いUIが作れるはずです。
Sponsor Link
[…] ここでつくった、ドラッグできるフレームに対して、マウスの位置に合わせてカーソルを変更しようと思ったのがきっかけでした。 […]