It took me a little time to figure ot how to draw a simple diamond using Xcode 16.0 and Swift 5. The trouble I ran into was the lines connecting the start and end point were not closing. I could fudge it a little by increasing the length of the start and end lines – BUT that wouldn’t really work because with width of the stroke changes the required fudge-factor.
The solution to the problem is calling closeSubpath() on your path variable to format the start/end match points properly.
Now, you wouldn’t see this problem if you were coding with fill or stroking in a different fashion than using .stroke or .strokeBorder as I did. If you were just filling then you don’t see the stroke lines. Also stroke works perfect with a 1 pixel line without calling closeSubpath(). It’s when the. line gets to be about 4 pixels wide that it becomes visible. To use those two methods, Diamond needed to conform to InsettableShape, and thus implement:
inset(by amount: CGFloat) -> some InsettableShape
You control the shape of the rendered diamond by placing it into a container and it will fit the size.
// // ContentView.swift // import SwiftUI struct ContentView: View { var body: some View { VStack { Diamond() .inset(by: 5) .fill(.red) .strokeBorder(.blue, lineWidth: 10) .frame(width:200, height:300) .background(.green) Diamond() .inset(by: 15) .fill(.orange) .strokeBorder(.black, lineWidth: 16) .frame(width:150, height:250) .background(.green) } .padding() .background(.yellow) } } #Preview { ContentView() }
// // // Diamond.swift // import SwiftUI import CoreGraphics // ------------------------------------------------------------------------- // We make diamond InsetableShape and add func required by protocol: inset() // then we can use stroke on shape and get an outline // ------------------------------------------------------------------------- struct Diamond: InsettableShape { var insetAmount = 0.0 func inset(by amount: CGFloat) -> some InsettableShape { var diamond = self diamond.insetAmount += amount return diamond } func path(in rect: CGRect) -> Path { // draws counter clockwise from 3 o'clock let start0 = CGPoint(x: rect.maxX - insetAmount, y: rect.midY) // 3 o'clock to 12 o'clock let end0 = CGPoint(x: rect.midX, y: rect.maxY - insetAmount) // 12 o'clock to 9 o'clock let end1 = CGPoint(x: rect.minX + insetAmount, y: rect.midY) // 9 o'clock to 6 o'clock let end2 = CGPoint(x: rect.midX, y: rect.minY + insetAmount) // 6 o'clock to 3 o'clock let end3 = CGPoint(x: rect.maxX - insetAmount, y: rect.midY) var p = Path() p.move(to: start0) p.addLines([start0, end0, end1, end2, end3]) // complete drawing the diamond by applying // closeSubpath - otherwise joints won't fill // in between start point and end point p.closeSubpath() return p } }