BS
BleepingSwift
Published on
4 min read

> Rounding Corners in SwiftUI: From cornerRadius to clipShape

If you've been working with SwiftUI for a while, you've probably used .cornerRadius() to round views. It was simple and it worked. But starting in iOS 17, Apple deprecated it in favor of the more flexible clipShape modifier. Let's look at what changed and how to update your code.

The Old Way: cornerRadius

The cornerRadius(_:antialiased:) modifier has been around since iOS 13. You'd use it like this:

Text("Hello, world!")
    .padding()
    .background(Color.blue)
    .cornerRadius(12)

It gets the job done, but it has limitations. Under the hood, it just clips the view to a RoundedRectangle, and it doesn't handle safe area insets properly. Apple deprecated it in iOS 17 with the message: "Use clipShape or fill instead."

The Modern Replacement: clipShape

The recommended replacement is .clipShape(_:), which accepts any Shape. The most direct migration looks like this:

Text("Hello, world!")
    .padding()
    .background(Color.blue)
    .clipShape(RoundedRectangle(cornerRadius: 12))

That's a bit verbose though. Thanks to a static convenience method on Shape, you can shorten it with dot syntax:

Text("Hello, world!")
    .padding()
    .background(Color.blue)
    .clipShape(.rect(cornerRadius: 12))

The .rect(cornerRadius:style:) shorthand works anywhere SwiftUI expects a shape, which keeps things concise.

Using Shapes with background

You can skip clipShape entirely by passing a shape directly to .background:

Text("Hello, world!")
    .padding()
    .background(.blue, in: .rect(cornerRadius: 12))

This fills the shape with your color and clips it in one step. It's clean and reads well, especially for simple cases.

Continuous vs Circular Corners

Both RoundedRectangle and the .rect shorthand accept a style parameter that controls how the corners curve. There are two options:

// Standard circular arcs
.clipShape(.rect(cornerRadius: 20, style: .circular))

// Smooth "squircle" curves matching iOS system UI
.clipShape(.rect(cornerRadius: 20, style: .continuous))

The .continuous style is what Apple uses for app icons, widgets, and system chrome throughout iOS. The curve transitions more smoothly into the straight edges, avoiding the subtle tangent discontinuity you get with .circular. If you want your views to feel native, .continuous is the way to go. The default is .circular, so you'll need to specify it explicitly.

struct ProfileCard: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 8) {
            Image(systemName: "person.circle.fill")
                .font(.largeTitle)
            Text("Jane Doe")
                .font(.headline)
            Text("iOS Developer")
                .font(.subheadline)
                .foregroundStyle(.secondary)
        }
        .padding()
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(.ultraThinMaterial, in: .rect(cornerRadius: 16, style: .continuous))
    }
}

cornerSize vs cornerRadius

RoundedRectangle has a second initializer that takes a CGSize instead of a radius:

RoundedRectangle(cornerSize: CGSize(width: 20, height: 10), style: .continuous)

This creates elliptical corners rather than circular ones, where the width and height of the corner curve can differ. It's uncommon in practice since most designs use equal values, but it's there if you need it for something like a ticket stub or tag shape.

Rounding Specific Corners

Sometimes you only need certain corners rounded, like the top of a card or one side of a tab. iOS 17 introduced UnevenRoundedRectangle for exactly this:

Text("Top corners only")
    .padding()
    .background(.orange)
    .clipShape(
        UnevenRoundedRectangle(
            topLeadingRadius: 16,
            bottomLeadingRadius: 0,
            bottomTrailingRadius: 0,
            topTrailingRadius: 16
        )
    )

I covered this in depth in my article on rounding specific corners, including custom shapes for pre-iOS 16, message bubbles, and combining with strokes.

ConcentricRectangle (iOS 26)

iOS 26 introduced ConcentricRectangle, a shape that automatically calculates its corner radius based on its distance from its container's edges. The idea is corner concentricity: when shapes are nested, their curved portions share the same center point, which looks more intentional than just eyeballing inset radii.

VStack {
    ConcentricRectangle()
        .fill(.blue.gradient)
        .padding(12)
}
.containerShape(.rect(cornerRadius: 24))

The inner shape's radius adjusts automatically so its corners align concentrically with the outer container. You define the outer shape with .containerShape(_:), and any ConcentricRectangle inside calculates the rest.

This is especially useful for card-in-card layouts:

struct ConcentricCard: View {
    var body: some View {
        ZStack {
            ConcentricRectangle()
                .fill(.ultraThinMaterial)

            VStack(spacing: 12) {
                ConcentricRectangle(corners: .concentric, isUniform: true)
                    .fill(.blue.gradient)
                    .frame(height: 120)

                Text("Automatic concentric corners")
                    .font(.headline)
                    .padding(.bottom)
            }
            .padding(12)
        }
        .frame(height: 200)
        .containerShape(.rect(cornerRadius: 24))
    }
}

The isUniform parameter ensures all corners get the same resolved radius, even if the shape is positioned asymmetrically within its container.

Migrating Existing Code

If you have a codebase full of .cornerRadius() calls, the migration is straightforward. For a quick find-and-replace approach, you could add a small extension:

extension View {
    func cornerRadius(_ radius: CGFloat) -> some View {
        clipShape(.rect(cornerRadius: radius))
    }
}

This silences the deprecation warnings and uses the modern API under the hood. Over time, you can replace individual call sites with clipShape or background(_:in:) directly, especially where you want to take advantage of corner styles or uneven radii.

For new code, prefer clipShape(.rect(cornerRadius:)) or passing a shape directly to background. They're both clearer about what's happening and give you access to the full range of shape options.

subscribe.sh

// Stay Updated

Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.

>

By subscribing, you agree to our Privacy Policy.