Shapes with Paths using SwiftUI Part1
A compendium of shapes using paths

At school we all learn geometry and trigonometry on a grid, which makes perfect sense— which from from a SwiftUI programming point of view is perhaps where it all starts to go wrong. You see the challenge you face in coding for iDevices is that not only do they come in all shapes and sizes, worse they’re constantly shape shifting, going from portrait to landscape and back again.
To address this challenge, Apple have decided in their SwiftUI language to try as hard as they can to distance us from co-ordinates, so no more grid references. To a great extend they made a warp jump to do so with the SwiftUI declarative language, with only one small hitch. Shapes.
The problem with shapes, being — well — is that they are intrinsically linked to co-ordinates and grids, at least the more complex ones. As such Apple decided to port the previous solution with UIPath over to SwiftUI, and there within lies the danger.
Now the client brief calls for a triangle. And I am going to present three options to draw one in this paper, the first the wrong way, the second the Apple text book way, the third well —in my view a better way — you need to read on to find out how. Let’s begin. Let’s start with the wrong way. This is the driver.
struct ContentView: View {
@State var sides = 4
var body: some View {
Triangle()
.stroke(Color.gray)
.frame(width: 256, height: 256)
.padding()
}
}With this the code the view behind it.
struct Triangle: Shape {
let midX = 64
let minX = 0
let maxX = 128
let minY = 0
let maxY = 128
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: midX, y: minY))
path.addLine(to: CGPoint(x: minX, y: maxY))
path.addLine(to: CGPoint(x: maxX, y: maxY))
path.addLine(to: CGPoint(x: midX, y: minY))
return path
}
}Now, no kidding this is going to draw a triangle, you hit it on the mark — with the only snag that it will be placed on the screen at specific co-ordinates. Which means it will be in a different place on different iPhones and indeed even on the same device as it shape shifts between portrait and landscape modes. Don’t do it like this.
Now the text book solution looks like this.
struct Triangle: 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.minX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.midX, y: rect.minY))
return path
}
}Now this is far better. A nice equilateral triangle. One which will be centered on every screen even when it shape shifts. Should you want to change the direction of the pointy bit [not to be too technical] you can simply rotate it using rotateEffect() primitive, its easy. You’re done, or are you.
Because wait— you know how it works. No sooner you show the client, than they’ll change their mind and want a different polygon. No — triangles are out and the rhombus is in, Jeez. Now you got two options here. You can either do a rhombus or you can put the code in place to do a polygon. Let’s do a polygon. A polygon because it is a better way to do a triangle too. This is our base code.







