SwiftUI Path.inset(in:) function

246 Views Asked by At

I'm trying to inset a path by a constant amount, but for some reason, it appears this is not working. What am I doing wrong?

This works fine; it draws a rectangle of 250x250 with a red border.

let frame = CGRect(origin: .zero, size: CGSize(width: 250, height: 250))

VStack {
    Path { path in
        path.addRect(frame)
    }
    .border(Color.red)
}.frame(width: frame.width, height: frame.height)

Now, I would like to inset this shape by an arbitrary amount, let's say 20. I would assume I can use the path(in:) method to do this, but this appears to be not working.

let frame = CGRect(origin: .zero, size: CGSize(width: 250, height: 250))

VStack {
    Path { path in
        path.addRect(frame)
    }
    .path(in: frame.insetBy(dx: 20, dy: 20))
    .border(Color.red)
}.frame(width: frame.width, height: frame.height)

I would assume to see a smaller rectangle of 230x230 with a red border, but instead I see a 250x250 rectangle with a red border. Why is this? How can I make it so the inset is applied correctly?

PS: This addRect(frame) function can of course be written as addRect(frame.insetBy(dx: 20, dy: 20)), but the rect is here as per demonstration. In practice I have a path with several lines and points that do not work with the insetBy(dx:dy:) function

1

There are 1 best solutions below

2
DonMag On

Quick example extension for using inset on a Path:

extension Path {
    func insetBy(dx: CGFloat, dy: CGFloat) -> Path {
        let wMultiplier = 1.0 - (dx / self.boundingRect.width)
        let hMultiplier = 1.0 - (dy / self.boundingRect.height)
        let tr = CGAffineTransform(scaleX: wMultiplier, y: hMultiplier).translatedBy(x: dx * 0.5, y: dy * 0.5)
        return self.applying(tr)
    }
}

struct TestView: View {
    
    let frame = CGRect(origin: .zero, size: CGSize(width: 250, height: 250))
    
    var body: some View {
        
        VStack {
            Path { path in
                path.addRect(frame)
            }
            .insetBy(dx: 20, dy: 20)
            .stroke(lineWidth: 2)
            .foregroundColor(Color.red)
            .background(Color.yellow)
        }
        .frame(width: frame.width, height: frame.height)
        
    }
    
}

I would, however, strongly suggest searching and reading up on other approaches. This looks very flexible: https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-uibezierpath-and-cgpath-in-swiftui