【SwiftUI】ドラッグでImageの位置を移動する
ドラッグで画面に配置した顔のパーツ達を移動したくなってきたので移動してみることにしました。
作ったもの
顔のパーツをドラッグすると位置を移動することができ、ドラッグを終了すると終了した位置に顔のパーツが配置されます。
環境
- Xcode 13.3
ドラッグで移動出来るViewを作成する
DraggableImage
というViewを作成しました。
import SwiftUI struct DraggableImage: View { @State private var location: CGPoint @State private var isDragging = false private let systemName: String init(_ systemName: String, defaultLocation: CGPoint) { self.systemName = systemName self.location = defaultLocation } /// Drag Gesture var dragGesture: some Gesture { DragGesture() .onChanged { value in location = value.location isDragging = true } .onEnded { _ in isDragging = false } } var body: some View { Image(systemName: systemName) .resizable() .scaledToFit() .foregroundColor(isDragging ? .blue : .black) .frame(width: 100) .position(location) .gesture(dragGesture) } }
DragGesture
ドラッグジェスチャーが行われた際、任意の処理を実行する為にDragGesture()
を使用します。
/// Drag Gesture var dragGesture: some Gesture { DragGesture() .onChanged { value in location = value.location isDragging = true } .onEnded { _ in isDragging = false } }
- onChanged
- ドラッグジェスチャーが行われて、
value: DragGesture.Value
が変更される度に実行される処理
- ドラッグジェスチャーが行われて、
- onEnded
- ドラッグジェスチャーが終了した際に実行される処理
今回はonChange
の際に、DragGesture.Value
から変更後のlocation
を取得出来るので、Image
のlocation
に変更後の値を代入しています。
DragGesture.Value
今回はlocation
しか使っていませんが、DragGesture.Value
のプロパティは他にもあります。
- location: CGPoint
- 現在のドラッグジェスチャーイベントのロケーション
- predictedEndLocation: CGPoint
- 現在のドラッグスピードに基づき、もし今ドラッグが停止したらこの位置になるであろうと予測されたロケーション
- predictedEndTranslation: CGSize
- 現在のドラッグスピードに基づき、もし今ドラッグが停止したらこのくらい移動するであろうと予測された移動量
- startLocation: CGPoint
- ドラッグジェスチャーが開始された最初のイベント時点でのロケーション
- time: Date
- 現在のドラッグジェスチャーイベントに基づく、時間
- translation: CGSize
- ドラッグジェスチャーの開始地点から現在のイベントの地点までの合計移動量
ImageにGestureとlocationを反映する
var body: some View { Image(systemName: imageName) .resizable() .scaledToFit() .foregroundColor(self.isDragging ? Color.blue : Color.black) .frame(width: 100) // Drag gestureでロケーションが変更されると反映される .position(location) // Drag gestureをアタッチ .gesture(dragGesture) }
Image
の.position
に@State
のlocation
を渡して、ドラッグジェスチャーでロケーションが変わる毎に反映されるようにしています。
@State private var location: CGPoint
ContentViewにDraggableViewを配置する
FaceParts
まずは顔のパーツの表現する為にFaceParts
というenum
を用意しました。
enum FaceParts: String, Identifiable, CaseIterable { case leftEye case rightEye case nose case mouth var id: String { rawValue } // ImageのsystemName var imageName: String { switch self { case .leftEye, .rightEye: return "eye" case .nose: return "nose" case .mouth: return "mouth" } } // Positionの初期値 var defaultPosition: CGPoint { switch self { case .leftEye: return CGPoint(x: 120, y: 200) case .rightEye: return CGPoint(x: 260, y: 200) case .nose: return CGPoint(x: 190, y: 300) case .mouth: return CGPoint(x: 190, y: 430) } } }
ContentView
用意したFaceParts
をForEach
で各顔のパーツを描画しています。
import SwiftUI struct ContentView: View { var body: some View { ZStack { Rectangle() .ignoresSafeArea() .foregroundColor(.paleOrange) ForEach(FaceParts.allCases) { DraggableImage($0.imageName, defaultLocation: $0.defaultPosition) } } } }
Color.paleOrange
今回は背景として使用しているRactangle()
の色をColor
のextension
で作成したpaleOrange
にしました。
extension Color { static let paleOrange = Color(red: 245 / 255, green: 218 / 255, blue: 195 / 255, opacity: 1) }
以上で完成です。
おわりに
SwiftUIでのドラッグジェスチャーを調査する為に作ってみましたが、もう少し一工夫すれば子どもに喜んでもらえそうな何かが作れそうな気がしました。
もう少し遊びながら調べていきたいと思います。