Reputation: 107
Can someone please explain why my self made "myRectangle" that I have conformed to InsettableShape doesn't work with .strokeBorder but the built in Rectangle() does?
Here is myRectangle code;
struct myRectangle: InsettableShape {
var insetAmount: CGFloat = 0
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.midX * 1.2, y: rect.maxY))
path.addLine(to: CGPoint(x: rect.midX * 1.2, y: rect.midY * 0.6))
path.addLine(to: CGPoint(x: rect.midX * 0.8, y: rect.midY * 0.6))
path.addLine(to: CGPoint(x: rect.midX * 0.8, y: rect.maxY))
return path
}
func inset(by amount: CGFloat) -> some InsettableShape {
var rectangle = self
rectangle.insetAmount -= amount
return rectangle
}
}
then modifying it with the .strokeBorder;
struct ColorCyclingRectangle: View {
var amount = 0.0
var steps = 100
var body: some View {
ZStack {
ForEach(0..<steps) { value in
myRectangle()
.inset(by: CGFloat(value))
.strokeBorder(self.color(for: value, brightness: 1), lineWidth: 2)
}
}
.drawingGroup()
}
func color(for value: Int, brightness: Double) -> Color {
var targetHue = Double(value) / Double(self.steps) + self.amount
if targetHue > 1 {
targetHue -= 1
}
return Color(hue: targetHue, saturation: 1, brightness: brightness)
}
}
I am expecting the rainbow effect but just get a gradient line.
I have tried modifying my path to - insetAmount but I just get a weird shape and the rainbow border is straight, not square
Am I misunderstanding somthing or does this modifyer only work on the default shapes?
Upvotes: 0
Views: 1658
Reputation: 6406
Though this is an older questions I will make an attempt to answer it in hopes the OP is still interested or that it may help others.
You can most definitely use the strokeBorder() modifier on custom types that conform to the InsettableShape protocol noting that it is your responsibility to write the code that insets - reduces size - of your custom shape accordingly.
The first item I noticed in your code is that you are hardcoding some values within your path(in rect: CGRect)
method which is why your output is not a square. Two options for this are:
path(in rect: CGRect)
to create the shaperect
and utilize the .frame() modifier on the container where you are creating your custom shape or on the shape directly to control its dimensions.I am not sure which is preferable, but I will use option 2 as it seems this is how the built-in shapes work - in relation to width and height at least - and it makes it easier to comprehend the insetting code as follows
struct myRectangle: InsettableShape {
var insetAmount: CGFloat = 0
func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: rect.minX + insetAmount, y: rect.maxY - insetAmount))
path.addLine(to: CGPoint(x: rect.maxX - insetAmount, y: rect.maxY - insetAmount))
path.addLine(to: CGPoint(x: rect.maxX - insetAmount, y: rect.minY + insetAmount))
path.addLine(to: CGPoint(x: rect.minX + insetAmount, y: rect.minY + insetAmount))
path.addLine(to: CGPoint(x: rect.minX + insetAmount, y: rect.maxY - insetAmount))
return path
}
func inset(by amount: CGFloat) -> some InsettableShape {
var rectangle = self
rectangle.insetAmount += amount
return rectangle
}
}
In the above you will note:
rect
sizepath(in rect: CGRect)
that creates the path. For example path.move(to: CGPoint(x: rect.minX + insetAmount, y: rect.maxY - insetAmount))
is the bottom-left point. To inset this point you need to move the X to the right rect.minX + insetAmount
and Y upwards rect.maxY - insetAmount
- we are subtracting from Y to move upwards per my point 2.In your ColorCyclingRectangle custom View I only added a .frame(width: 300, height: 300)
modifier to the ZStack container that you are creating your custom shape in so that I can control its size.
struct ColorCyclingRectangle: View {
var amount = 0.0
var steps = 100
var body: some View {
ZStack {
ForEach(0..<steps) { value in
myRectangle()
.inset(by: CGFloat(value))
.strokeBorder(self.color(for: value, brightness: 1), lineWidth: 2)
}
}
.drawingGroup()
.frame(width: 300, height: 300)
}
func color(for value: Int, brightness: Double) -> Color {
var targetHue = Double(value) / Double(self.steps) + self.amount
if targetHue > 1 {
targetHue -= 1
}
return Color(hue: targetHue, saturation: 1, brightness: brightness)
}
}
With this I was able to get the rainbow effect.
Hopefully this resolves your issue.
Upvotes: 3