Skip to main content

How to fill and stroke shapes at the same time

About 3 minSwiftSwiftUIArticle(s)bloghackingwithswift.comcrashcourseswiftswiftuixcodeappstore

How to fill and stroke shapes at the same time 관련

SwiftUI by Example

Back to Home

How to fill and stroke shapes at the same time | SwiftUI by Example

How to fill and stroke shapes at the same time

Updated for Xcode 15

Improved in iOS 17

In iOS 17 and later, you can fill and stroke shapes at the same time just by stacking the modifiers, like this:

Circle()
    .stroke(.red, lineWidth: 20)
    .fill(.orange)
    .frame(width: 150, height: 150)

Download this as an Xcode projectopen in new window

A orange circle with a red outline.
A orange circle with a red outline.

This even works with multiple strokes of various sizes:

Circle()
    .stroke(.blue, lineWidth: 45)
    .stroke(.green, lineWidth: 35)
    .stroke(.yellow, lineWidth: 25)
    .stroke(.orange, lineWidth: 15)
    .stroke(.red, lineWidth: 5)
    .frame(width: 150, height: 150)

Download this as an Xcode projectopen in new window

A circle with a rainbow of stroke colors.
A circle with a rainbow of stroke colors.

In iOS 16 and below, SwiftUI provides the fill(), stroke(), and strokeBorder() modifiers for adjusting the way we draw shapes, but it does not provide a built-in way to fill and stroke at the same time. However, we can get the same effect in two different ways, and I'm going to show you both here.

The first option is to use strokeBorder() to add a border around your shape, then place a filled shape in the background using background(). For example, this creates a circle with a black stroke and blue fill:

Circle()
    .strokeBorder(.red, lineWidth: 20)
    .background(Circle().fill(.orange))
    .frame(width: 150, height: 150)

Download this as an Xcode projectopen in new window

A blue circle with a thick black outline.
A blue circle with a thick black outline.

Using background() ensures the blue circle always matches the size of the red circle.

The second option is to layer the two circles manually using ZStack:

ZStack {
    Circle()
        .fill(.orange)

    Circle()
        .strokeBorder(.red, lineWidth: 20)
}
.frame(width: 150, height: 150)

Download this as an Xcode projectopen in new window

If you want to fill and stroke lots of shapes, you should consider wrapping up this functionality in an extension. Only InsettableShapes get the strokeBorder() method, so you should probably write two extension methods – one to handle regular shapes using stroke(), and one to handle insettable shapes using strokeBorder().

Here's how that looks in code:

extension Shape {
    func fill<Fill: ShapeStyle, Stroke: ShapeStyle>(_ fillStyle: Fill, strokeBorder strokeStyle: Stroke, lineWidth: Double = 1) -> some View {
        self
            .stroke(strokeStyle, lineWidth: lineWidth)
            .background(self.fill(fillStyle))
    }
}

extension InsettableShape {
    func fill<Fill: ShapeStyle, Stroke: ShapeStyle>(_ fillStyle: Fill, strokeBorder strokeStyle: Stroke, lineWidth: Double = 1) -> some View {
        self
            .strokeBorder(strokeStyle, lineWidth: lineWidth)
            .background(self.fill(fillStyle))
    }
}
Similar solutions…
How to make two gestures recognize at the same time using simultaneousGesture() | SwiftUI by Example

How to make two gestures recognize at the same time using simultaneousGesture()
How to combine shapes to create new shapes | SwiftUI by Example

How to combine shapes to create new shapes
How to make two views the same width or height | SwiftUI by Example

How to make two views the same width or height
SwiftUI's built-in shapes | SwiftUI by Example

SwiftUI's built-in shapes
How to display solid shapes | SwiftUI by Example

How to display solid shapes

이찬희 (MarkiiimarK)
Never Stop Learning.