I'm confused how I add a border with a stroke color and line width to a SwiftUI View using clipShape. Basically, I just want to have rounded corners with a border color.
Rectangle()
.frame(width: 80, height: 80)
.foregroundStyle(.gray)
.clipShape(
RoundedRectangle(cornerRadius: 6)
)
Unfortunately, you cannot simply apply .border
in combination with .clipShape
, because this always draws a square border:
.border
is applied before .clipShape
then it is clipped at the corners. You notice it more when the corner radius is large..border
is applied after .clipShape
then the corners of the border are square.So to add a border around a clipped shape, you need to stroke it as a separate operation using the same shape. An overlay works well:
Rectangle()
.frame(width: 80, height: 80)
.foregroundStyle(.gray)
.clipShape(
RoundedRectangle(cornerRadius: 6)
)
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(.red, lineWidth: 2)
)
The question was about using .clipShape
. However, if you just want to show a filled shape with a border in the background of a view, it can be done without using .clipShape
.
→ Add the shape in the background, fill it with a color or gradient, then stroke its border.
Pre iOS 17, the border needs to be stroked using a second shape definition, as shown above.
For example, if the foreground content would be in an HStack
, the background can be applied as follows:
HStack {
// ... foreground content
}
.padding()
.background {
RoundedRectangle(cornerRadius: 6)
.fill(.gray)
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(.red, lineWidth: 2)
)
}
This is not much different to what we had before, except that an unnecessary clip operation is avoided.
Since iOS 17, a different .fill
overload is available, see fill(_:style:). Now the shape only needs to be defined once:
HStack {
// ... foreground content
}
.padding()
.background {
RoundedRectangle(cornerRadius: 6)
.fill(.gray)
.stroke(.red, lineWidth: 2)
}