Scaling UIBezierPath for SwiftUI Shapes

Quick tip for today! What happens when you get an SVG output from your designer or design program? How do you convert that over into a custom SwiftUI Shape struct?

Converting SVG Path

  1. First you can use the popular tool SwiftVG created by Mike Engel to convert the SVG path code into Swift UIBezierPath code.
  2. From there you'll need to scale UIBezierPath to fit the CGRect that is provided to your custom Shape struct in the path(in rect:) function. You can do this using an extension on Path to apply a CGAffineTransform. From there you can return the Path as normal.
struct Wave: Shape {
    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath()
        
        // Path drawing code here
        // ...
        
        return Path(path.cgPath).scaled(for: rect)
    }
}

extension Path {
    func scaled(for rect: CGRect) -> Path {
        let scaleX = rect.width/boundingRect.width
        let scaleY = rect.height/boundingRect.height
        let scale = min(scaleX, scaleY)
        return applying(CGAffineTransform(scaleX: scale, y: scale))
    }
}

Scaling in X or Y Direction

If you'd like to further scale the path in only one direction, then you can again write an extension for Path. By performing a CGAffineTransform on the Path you can achieve the desired result.

struct Wave: Shape {
    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath()
        
        // Path drawing code here
        // ...
        
        return Path(path.cgPath).scaled(for: rect).scale(x: 1, y: 5)
    }
}

extension Path {
    func scale(x: CGFloat, y: CGFloat) -> Path {
        return applying(CGAffineTransform(scaleX: x, y: y))
    }
}
Wave Scaled in Y direction only