skip to Main Content

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")
            }
        }
    }
}

2

Answers


  1. 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)
    
    Login or Signup to reply.
  2. 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")
                }
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search