これは備忘録メモ
SwiftUIでアノテーションツールを作ろうかと思ったらオブジェクトのドラッグ&ドロップのやり方がよくわからなくてハマる。
次に値を取ってこようとしたら、慣れていないのでBindingの書き方がわからなくて値を取ってこれない。
ちょっとしたことも慣れてないと難しい。
import SwiftUI
extension View {
func draggable(into binding: Binding<CGPoint>) -> some View {
self.modifier(DraggableModifier(offset: binding))
}
}
struct DraggableModifier: ViewModifier {
@Binding var offset: CGPoint
@State private var previousTranslation: CGSize?
func body(content: Content) -> some View {
content
.offset(x: offset.x, y: offset.y)
.gesture(DragGesture().onChanged { value in
if let translation = previousTranslation {
// Drag changed
let delta = CGSize(width: value.translation.width - translation.width,
height: value.translation.height - translation.height)
offset.x += delta.width
offset.y += delta.height
previousTranslation = value.translation
} else {
// Drag started
previousTranslation = value.translation
}
}
.onEnded { _ in
// Drag ended
previousTranslation = nil
})
}
}
struct Shape {
var offset: CGPoint = .zero
var view: AnyView
init(_ view: AnyView){
self.view = view
}
}
struct ContentView: View {
@State private var location: CGPoint = CGPoint(x: 0, y: 0)
@State var offset: CGPoint = .zero
@State private var shapes: [Shape] = [
Shape(
AnyView (
Rectangle().fill(Color.red)
.frame(width: 100, height: 100)
)),
Shape(
AnyView (
Circle().fill(Color.blue)
.frame(width: 100, height: 100)
))
]
var body: some View {
ZStack {
Rectangle()
.fill(Color.clear)
VStack {
ForEach(shapes.indices, id: \.self) {
shapes[$0].view.draggable(into: $shapes[$0].offset)
Text("Offset: \(shapes[$0].offset.x)")
}
}
}.contentShape(Rectangle())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
修正版
前回のoffset版では、タップした位置の座標は返すがオブジェクトの座標を返していなかったので修正
import SwiftUI
extension View {
func draggable(into binding: Binding<CGPoint>) -> some View {
self.modifier(DraggableModifier(location: binding))
}
}
struct DraggableModifier: ViewModifier {
@Binding var location: CGPoint
@GestureState private var startLocation: CGPoint? = nil
func body(content: Content) -> some View {
content
.position(x: location.x, y: location.y)
.gesture(
DragGesture()
.onChanged { value in
var newLocation = startLocation ?? location
newLocation.x += value.translation.width
newLocation.y += value.translation.height
self.location = newLocation
}
.updating($startLocation) { (value, startLocation, transaction) in
startLocation = startLocation ?? location
}
)
}
}
struct Shape {
var location: CGPoint = .zero
var view: AnyView
init(_ view: AnyView, location: CGPoint){
self.view = view
self.location = location
}
}
struct ContentView: View {
@State private var shapes: [Shape] = [
Shape(
AnyView (
Rectangle().fill(Color.red)
.frame(width: 50, height: 50).offset(x: 25,y: 25)
), location: CGPoint(x: 0, y: 0)),
Shape(
AnyView (
Circle().fill(Color.blue)
.frame(width: 50, height: 50).offset(x: 25,y: 25)
), location: CGPoint(x: 100, y: 100))
]
var body: some View {
ZStack {
Rectangle()
.fill(Color.clear)
ForEach(shapes.indices, id: \.self) {
shapes[$0].view.draggable(into: $shapes[$0].location)
}
VStack {
ForEach(shapes.indices, id: \.self) {
Text("Location: \(shapes[$0].location.x), \(shapes[$0].location.y)")
}
}
}.contentShape(Rectangle())
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
参考サイト
- Drag - Using SwiftUI Gestures - Draw SwiftUI Part 1
- Binding
- Move your view around with Drag Gesture in SwiftUI