- Published on
> How to Round Specific Corners of a View in SwiftUI
- Authors

- Name
- Mick MacCallum
- @0x7fs
SwiftUI's RoundedRectangle rounds all four corners equally, but sometimes you only want to round specific corners—like the top corners of a card or the bottom corners of a header. iOS 16 introduced UnevenRoundedRectangle for exactly this purpose, though you can also create custom shapes for earlier iOS versions.
Using UnevenRoundedRectangle (iOS 16+)
The easiest approach on iOS 16 and later is UnevenRoundedRectangle, which lets you specify a different radius for each corner:
Text("Top Corners Only")
.padding()
.frame(maxWidth: .infinity)
.background(Color.blue)
.clipShape(
UnevenRoundedRectangle(
topLeadingRadius: 20,
bottomLeadingRadius: 0,
bottomTrailingRadius: 0,
topTrailingRadius: 20
)
)
You can also use the corner radii initializer for a more compact syntax:
UnevenRoundedRectangle(
cornerRadii: .init(
topLeading: 20,
bottomLeading: 0,
bottomTrailing: 0,
topTrailing: 20
)
)
Creating a Custom Shape for Earlier iOS
For iOS 15 and earlier, create a custom Shape that uses UIBezierPath:
struct RoundedCorner: Shape {
var radius: CGFloat = .infinity
var corners: UIRectCorner = .allCorners
func path(in rect: CGRect) -> Path {
let path = UIBezierPath(
roundedRect: rect,
byRoundingCorners: corners,
cornerRadii: CGSize(width: radius, height: radius)
)
return Path(path.cgPath)
}
}
Use it with .clipShape():
Text("Custom Corners")
.padding()
.background(Color.green)
.clipShape(RoundedCorner(radius: 16, corners: [.topLeft, .topRight]))
You can create a convenient view extension:
extension View {
func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
clipShape(RoundedCorner(radius: radius, corners: corners))
}
}
// Usage
Text("Bottom Corners")
.padding()
.background(Color.orange)
.cornerRadius(16, corners: [.bottomLeft, .bottomRight])
Common Patterns
Rounding the top corners is useful for sheets and cards that slide up from the bottom:
struct BottomSheet: View {
var body: some View {
VStack {
Capsule()
.fill(Color.secondary.opacity(0.5))
.frame(width: 40, height: 5)
.padding(.top, 8)
Text("Sheet Content")
.padding()
Spacer()
}
.frame(maxWidth: .infinity)
.background(Color(.systemBackground))
.clipShape(
UnevenRoundedRectangle(
topLeadingRadius: 20,
bottomLeadingRadius: 0,
bottomTrailingRadius: 0,
topTrailingRadius: 20
)
)
.shadow(radius: 10)
}
}
For message bubbles where corners vary based on the sender:
struct MessageBubble: View {
let text: String
let isFromCurrentUser: Bool
var body: some View {
Text(text)
.padding(12)
.background(isFromCurrentUser ? Color.blue : Color(.systemGray5))
.foregroundStyle(isFromCurrentUser ? .white : .primary)
.clipShape(
UnevenRoundedRectangle(
topLeadingRadius: 16,
bottomLeadingRadius: isFromCurrentUser ? 16 : 4,
bottomTrailingRadius: isFromCurrentUser ? 4 : 16,
topTrailingRadius: 16
)
)
}
}
Combining with Strokes
To add a border to a shape with specific rounded corners, use the shape as both the clip and the stroke:
struct StrokedCard: View {
let cornerRadii = RectangleCornerRadii(
topLeading: 20,
bottomLeading: 0,
bottomTrailing: 0,
topTrailing: 20
)
var body: some View {
Text("Card Content")
.padding()
.frame(maxWidth: .infinity)
.background(Color.white)
.clipShape(UnevenRoundedRectangle(cornerRadii: cornerRadii))
.overlay(
UnevenRoundedRectangle(cornerRadii: cornerRadii)
.stroke(Color.blue, lineWidth: 2)
)
}
}
Different Radii for Each Corner
Sometimes each corner needs a completely different radius. UnevenRoundedRectangle handles this naturally:
struct AsymmetricCard: View {
var body: some View {
VStack(alignment: .leading) {
Text("Asymmetric Design")
.font(.headline)
Text("Each corner has a different radius")
.font(.subheadline)
.foregroundStyle(.secondary)
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
.background(
LinearGradient(
colors: [.purple, .blue],
startPoint: .topLeading,
endPoint: .bottomTrailing
)
)
.foregroundStyle(.white)
.clipShape(
UnevenRoundedRectangle(
topLeadingRadius: 30,
bottomLeadingRadius: 5,
bottomTrailingRadius: 20,
topTrailingRadius: 10
)
)
}
}
Performance Note
Using clipShape is efficient for most use cases. However, if you're clipping many views in a scrolling list, consider whether you truly need selective corner rounding or if a uniform cornerRadius modifier would suffice. The standard RoundedRectangle is slightly more optimized since it doesn't need to handle asymmetric cases.
For static layouts and moderate numbers of views, the performance difference is negligible. Use whatever shape best serves your design.
Continuous vs Circular Corners
iOS uses "continuous" corner curves (also called squircles) in many system components, which look smoother than circular arcs. Both RoundedRectangle and UnevenRoundedRectangle support this:
UnevenRoundedRectangle(
topLeadingRadius: 20,
bottomLeadingRadius: 0,
bottomTrailingRadius: 0,
topTrailingRadius: 20,
style: .continuous // Smoother Apple-style corners
)
The .continuous style matches the corner style of app icons and system UI elements, while .circular uses traditional circular arcs.
// Continue_Learning
How to Resize an Image in SwiftUI
Learn how to resize images in SwiftUI using resizable(), frame(), and different content modes like scaledToFit and scaledToFill.
How to Change the Background Color of a View in SwiftUI
Learn different ways to set background colors on SwiftUI views, from simple color fills to gradients and materials.
Add Spacing to Toolbars with ToolbarSpacer in SwiftUI
Learn how to use ToolbarSpacer to add fixed and flexible spacing between toolbar items in SwiftUI, new in iOS 26.
// Stay Updated
Get notified when I publish new tutorials on Swift, SwiftUI, and iOS development. No spam, unsubscribe anytime.