I have a rectangle and I want to give it a stroke, but I that gives the whole rectangle a stroke. I want the stroke to represent a percentage, so like 50% only half the rectangle would be stroked
VStack {
Text(dayAbbreviation)
.foregroundStyle(Color("dark_grey"))
.font(.title)
Text(String(day))
.padding(.horizontal)
.font(.largeTitle)
Text(percentageCompleted + "%")
.font(.footnote)
.foregroundStyle(Color("dark_grey"))
.bold()
.padding(.bottom, 2)
}.background {
Rectangle()
.fill(.white)
.cornerRadius(3.0)
.aspectRatio(contentMode: .fill)
}
.padding(8)
.background {
Rectangle()
.fill(.blue)
.cornerRadius(3.0)
.aspectRatio(contentMode: .fill)
}
2
Answers
Note: From your question it is not completely clear how you want to make your stroke as a progress. I will assume that you wish to stroke it as if you were drawing. You start at one point and you "circle" around the center to create a rectangle.
There is a general solution where you can clip your whole view using
clipShape
on any of your views. This could be an image or a stroked rectangle or anything. And you can make it as a modifier. See the following:And it becomes as simple as
But the solution is not very pretty on a shape such as rectangle. If you animated the progress you would see the line is "moving" faster near the corners. Also there is not much you can do with edges being cut very sharply:
So to make it very nice and smooth you will need to construct the path out of lines so that you can define progress correctly plus you can control how the stroke is being drawn. It is a bit more work but it can be done like so:
And the result looks way nicer:
You didn’t describe, how exactly the partial rectangle should look. However, the text in the foreground of your example is a date, so I am guessing you want the border around the text to reflect the amount of the day that has elapsed. And since this relates to actual time-of-day, it probably makes sense if it starts drawing at 12 o’clock and goes clockwise, with the position for the end-of-stroke reflecting the current time of day on a 24-hour clock.
One way to do this is to trim the rectangle and then use
.stroke
instead of.fill
– see Why does the behavior of .trim() and .stroke() modifiers change on a Circle, depending on their order? for an explanation of why it doesn’t work with.fill
. However, a stroke normally starts in the top-left corner, so to make it start at 12 o’clock it means adding 12.5% to the percent completed. This works fine, until the percent completed exceeds 87.5, because it won’t stroke past 100%. So for the last part of the border between 87.5% and 100% it is necessary to use a second stroke.Like this:
What you might notice, is that the start and end points go over the exact position. This is because the stroke style uses a round line cap and this cap is drawn with its center-point at the calculated position. This causes the end of the stroke to go over the exact point by half the line width. This can be corrected by using a
GeometryReader
to obtain the size of the frame and applying a small correction to the trim value: