I want to make an app showing a graph with multitouch. When you drag one node, an edge connecting it with another node should follow it. My problem is that I don't know how to save information about node position in the node struct from drag gesture. I appreciate all the tips and Merry Christmas to everyone :)
Main code:
import SwiftUI
struct ContentView: View {
@State var nodes = Nodes()
@State var matrix = Matrix()
var body: some View {
ZStack {
ForEach(0..<matrix.arr.count) {x in
ForEach(0..<matrix.arr[x].count){y in
if matrix.arr[x][y] == 1 {
makeLine(from: nodes.n_array[x], to: nodes.n_array[y])
}
}
}
ForEach(nodes.n_array, id: \.id) {
$0.drawShape()
.draggable()
$0.label()
.draggable()
}
}
}
}
Drag gesture:
import SwiftUI
extension View {
func draggable() -> some View {
self
.modifier(DraggableModifier())
}
}
struct DraggableModifier: ViewModifier {
@State var previousTranslation: CGSize?
@State public var offset: CGPoint = .zero
func body(content: Content) -> some View {
content
.offset(x: offset.x, y: offset.y)
.gesture(DragGesture().onChanged { value in
if let translation = previousTranslation {
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 {
previousTranslation = value.translation
}
}
.onEnded { _ in
previousTranslation = nil
})
}
}
Edge drawing function:
import Foundation
import SwiftUI
func makeLine(from: Node, to: Node) -> some View {
return Path() { path in
path.move(to: CGPoint(x: from.x, y: from.y))
path.addLine(to: CGPoint(x: to.x, y: to.y))
}
.stroke(Color(red: 0.25, green: 0.25, blue: 0.25), lineWidth: 4)
}
Node structure:
import Foundation
import SwiftUI
struct Node: Decodable, Hashable {
public let id: Int
public var x: Double
public var y: Double
public let text: String
public var red: Double
public var green: Double
public var blue: Double
public var shape: Int
}
extension Node {
func circle() -> some View {
Circle().fill(Color(red: red, green: green, blue: blue)).frame(width: 64, height: 64, alignment: .center).position(x: x, y: y)
}
func square() -> some View {
Rectangle().fill(Color(red: red, green: green, blue: blue)).frame(width: 64, height: 64, alignment: .center).position(x: x, y: y)
}
@ViewBuilder func drawShape() -> some View {
switch shape{
case 1: circle()
case 2: square()
default: Text("Failed")
}
}
func label() -> some View {
return Text(text)
.font(.system(size: 30))
.bold()
.position(x: x, y: y)
.foregroundColor(Color(red: 0.25, green: 0.25, blue: 0.25))
}
}
Array of nodes:
import Foundation
class Nodes: ObservableObject {
public var n_array: [Node]
init() {
let url = Bundle.main.url(forResource: "nodes", withExtension: "json")!
let data = try! Data(contentsOf: url)
n_array = try! JSONDecoder().decode([Node].self, from: data)
}
}
Edge structure:
import Foundation
struct Edge: Decodable {
public let n1: Int
public let n2: Int
}
Array of edges:
import Foundation
import SwiftUI
class Edges {
public let e_array: [Edge]
init() {
let url = Bundle.main.url(forResource: "edges", withExtension: "json")!
let data = try! Data(contentsOf: url)
e_array = try! JSONDecoder().decode([Edge].self, from: data)
}
}
Matrix with conections:
import Foundation
class Matrix: ObservableObject {
private let nodes = Nodes()
private let edges = Edges()
public var arr: [[Int]]
init() {
arr = Array(repeating: Array(repeating: 0, count: nodes.n_array.count), count: nodes.n_array.count)
var x:Int, y: Int
for i in 0...edges.e_array.count-1 {
x = edges.e_array[i].n1
y = edges.e_array[i].n2
arr[x][y] = 1
}
}
}