Sergio Bost
Sergio Bost

Reputation: 3239

Is there a way to add paths to an already rendered path

TL;DR - I would like to add a Path or a Shape ? to an existing Shape and would like to know if I can somehow use path.addPath(...) See image for a visual of what I mean

Box Images

I posted a question earlier about this but don't think I phrased it well and very possibly went about it the wrong way. All I would like to do is to add shapes to an existing shape. I see that Path has a method addPath(:) that lets us add one path to another.. and that would be perfect if I could figure out where to do it at. Here is my current attempt:

import SwiftUI

struct ContentView: View {
    @State private var path = Path()
    var body: some View {
        VStack {
            MyShape()
                .stroke(lineWidth: 3)
                .padding()
            HStack {
                AddHorizontalLineView()
                AddVerticalLineView()
                AddSquareView()
                AddDiagonalLineView()
                
            }
        }
    }
    
    private func addBoxToPath(_ path: Path) -> Path { <-- No where to call this
        var workingPath = path
        workingPath.addRect(workingPath.boundingRect)
        return workingPath
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct MyShape: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.addRect(rect)
        return path
    }
}

struct AddHorizontalLineView: View {
    
    var body: some View {
        Button(action: {}){
            Rectangle()
                .stroke()
                .overlay(
                    HorizontalWire()
                        .stroke()
                        .padding()
                    
                )
        }.frame(width: 50, height: 50)
    }
    
}

struct AddVerticalLineView: View {
    
    var body: some View {
        Button(action: { }){
            Rectangle()
                .stroke()
                .overlay(
                    VerticalWire()
                        .stroke()
                        .padding()
                )
        }.frame(width: 50, height: 50)
    }
}

struct AddSquareView: View {
    var body: some View {
        Button(action: {
            
        }){
            Rectangle()
                .stroke()
                .overlay(
                    Rectangle()
                        .stroke()
                        .padding()
                )
        }.frame(width: 50, height: 50)
    }
}

struct AddDiagonalLineView: View {
    var body: some View {
        Button(action: {}){
            Rectangle()
                .stroke()
                .overlay(
                    DiagonalWire()
                        .stroke()
                        .foregroundColor(.red)
                        .padding()
                )
        }
        .frame(width: 50, height: 50)
    }
}

struct DiagonalWire: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.minX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
        return path
    }
}

struct VerticalWire: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.midX, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.midX, y: rect.maxY))
        return path
    }
}

struct HorizontalWire: Shape {
    func path(in rect: CGRect) -> Path {
        var path = Path()
        path.move(to: CGPoint(x: rect.minX, y: rect.midY))
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.midY))
        return path
    }
}

Can paths be added dynamically like this or the entire setup needs to be done beforehand in path(in:) method of every type that conforms to Shape

I hope my question makes more sense now..

Upvotes: 0

Views: 337

Answers (1)

jnpdx
jnpdx

Reputation: 52565

You can pass the Path into MyShape (although it really has very little point then -- you could just render the Path itself).

Here's a simple example (with some of your buttons removed since it'll be a very amount of work to hook up all of the logic). I also had to add an additional rect to the Path to start with, since your addBoxToPath depends on there being something in the Path so that the boundingBox isn't just a 0x0 rect:

struct ContentView: View {
    @State private var path = Path()
    var body: some View {
        VStack {
            MyShape(path: path)
                .stroke(lineWidth: 3)
                .padding()
            HStack {
                Button(action: {
                    path = addBoxToPath(path)
                }) {
                    Text("add")
                }
            }
        }.onAppear {
            path.addRect(CGRect(origin: .zero, size: CGSize(width: 200, height: 200)))
        }
    }
    
    private func addBoxToPath(_ path: Path) -> Path {
        var workingPath = path
        workingPath.addRect(workingPath.boundingRect.insetBy(dx: -5, dy: -5))
        return workingPath
    }
}

struct MyShape: Shape {
    var path : Path
    
    func path(in rect: CGRect) -> Path {
        return path
    }
}

Upvotes: 2

Related Questions