I am making an app where I am generating points with random position. I want to connect first point with the second one with the line, and that the second with the third, etc. My issue is that it is connecting second with the third but in the same time the first is being connected with the third one. And I don't want that. Please help.
This is my code:
struct Dot: Hashable {
var x: CGFloat
var y: CGFloat
}
struct TestShit: View {
@State var lastx = 0.0
@State var lasty = 0.0
@State var dots = [Dot]()
var body: some View {
VStack{
ZStack{
ForEach(dots, id: \.self){ dot in
Circle()
.frame(width: 5, height: 5)
.position(x: dot.x, y: dot.y)
Path { path in
path.move(to: CGPoint(x: lastx, y: lasty))
path.addLine(to: CGPoint(x: dot.x, y: dot.y))
}.stroke(.green, lineWidth: 5)
}
}
Spacer()
Button {
let randx = CGFloat.random(in: 100...300)
let randy = CGFloat.random(in: 100...300)
dots.append(Dot(x: randx, y: randy))
lastx = randx
lasty = randy
} label: {
Text("Button")
}
}
}
}
CodePudding user response:
Here I only maintain 1 path, and append to it each time rather than more than 1 path in the ForEach
import Combine
import SwiftUI
struct Dot: Hashable {
var x: CGFloat
var y: CGFloat
}
class Model: ObservableObject {
@Published
var dots: [Dot] = []
@Published
var path: UIBezierPath?
func addDot(x: CGFloat, y: CGFloat) {
dots.append(Dot(x: x, y: y))
let randomPoint = CGPoint(x: x, y: y)
guard let path = path else {
path = UIBezierPath()
path?.move(to: randomPoint)
path?.lineWidth = 2
return
}
path.addLine(to: CGPoint(x: x, y: y))
}
}
struct TestIt: View {
@ObservedObject
var model: Model
var body: some View {
VStack{
ZStack{
if let path = model.path {
Path(path.cgPath)
.stroke(.green, lineWidth: 5)
}
ForEach(model.dots, id: \.self){ dot in
Circle()
.frame(width: 5, height: 5)
.position(x: dot.x, y: dot.y)
}
}
Spacer()
Button {
let randx = CGFloat.random(in: 100...300)
let randy = CGFloat.random(in: 100...300)
model.addDot(x: randx, y: randy)
} label: {
Text("Button")
}
}
}
}
You might want something a little nicer looking like
Path(path.cgPath.copy(strokingWithWidth: 5, lineCap: .round, lineJoin: .round, miterLimit: 0))
.stroke(.green, lineWidth: 5)
CodePudding user response:
The problem is that with this loop:
Path { path in
path.move(to: CGPoint(x: lastx, y: lasty))
path.addLine(to: CGPoint(x: dot.x, y: dot.y))
}.stroke(.green, lineWidth: 5)
you are creating multiple line segments by moving to the same point, and then adding a line to a different point.
What you want to do is:
- set a "start" point
- move to that point
- for each dot, add a line to the next dot
Take a look at the difference here:
import SwiftUI
struct Dot: Hashable {
var x: CGFloat
var y: CGFloat
}
struct DotsView: View {
// initialize start point
var startX = CGFloat.random(in: 100...300)
var startY = CGFloat.random(in: 100...300)
@State var dots = [Dot]()
var body: some View {
VStack{
ZStack{
Path { path in
// move to start point
path.move(to: CGPoint(x: startX, y: startY))
dots.forEach { dot in
// add line to each dot
path.addLine(to: CGPoint(x: dot.x, y: dot.y))
}
}.stroke(.green, lineWidth: 5)
// draw the start point circle in red
Circle()
.fill(Color.red)
.frame(width: 5, height: 5)
.position(x: startX, y: startY)
// draw each dot circle in blue
ForEach(dots, id: \.self){ dot in
Circle()
.fill(Color.blue)
.frame(width: 5, height: 5)
.position(x: dot.x, y: dot.y)
}
}
Spacer()
Button {
let randx = CGFloat.random(in: 100...300)
let randy = CGFloat.random(in: 100...300)
// add a dot point
dots.append(Dot(x: randx, y: randy))
} label: {
Text("Button")
}
}
}
}