How to add Rounded Corners, Rounded Borders, and Shadows to SwiftUI views

How to add Rounded Corners, Rounded Borders, and Shadows to SwiftUI views
A French cabinet with classic flowerpots, ashtrays and wine glasses. Hortusplantsoen, circa 2022

SwiftUI offers a convenient and quick way to design UI elements for your iOS app. However, some things in SwiftUI are more complicated than UIKit. In today's blog post, we are going to look at one such feature and learn how to add rounded corners, rounded borders, and shadows to SwiftUI view.

This is a frequent use case when you're developing UI elements and I hope this short blog post will give you quick tips on how to design these elements for the given SwiftUI view instance without much hassle.

  1. Adding rounded corners

To add rounded corners to a given view, we will use RoundedRectangle API. To this API, we will pass the corner radius, and optionally pass the fill color to fill the background color of the input view


struct RoundedCornerWithShadow: View {
    var body: some View {
        Text("Hello World")
            .padding()
            .background(
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color.green)
            )
    }
}
View with rounded corners

2. Adding a Shadow

In the second part, we will add a shadow to our view. We will use shadow modifier on the view passing shadow radius and color.


struct RoundedCornerWithShadow: View {
    var body: some View {
        Text("Hello World")
            .padding()
            .background(
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color.green)
                    .shadow(color: Color.black.opacity(0.5), radius: 12, x: 0, y: 0)
            )
    }
}
View with rounded corners and shadow

3. Adding a border

In the third and final part, we will add a border to a rounded-corner rectangle. We will use the overlay API which will overlay our rounded-corner view with rectangles with the same styles and border with input border width and color.


struct RoundedCornerWithShadow: View {
    var body: some View {
        Text("Hello World")
            .padding()
            .background(
                RoundedRectangle(cornerRadius: 12)
                    .fill(Color.green)
                    .shadow(color: Color.black.opacity(0.5), radius: 12, x: 0, y: 0)
            )
            .overlay(
                RoundedRectangle(cornerRadius: 12)
                    .stroke(Color.red, lineWidth: 1)
            )
    }
}
View with rounded corners, border and shadow

Creating a View Modifier

The above code works like a charm, but it's not reusable. If we want to apply these styles to many more views, we need to copy-paste all the styles and change the parameters.

Let's make this code reusable by wrapping it in the view modifier. That way it stays in just one place and we don't need to apply updates in multiple places. We can simply pass the style parameters in the view modifier initializer and the modifier takes care of applying desired styles.


// An utility view extension
extension View {
    @ViewBuilder
    func `if`<Transform: View>(_ condition: Bool, transform: (Self) -> Transform) -> some View {
        if condition { transform(self) }
        else { self }
    }
}

// View modifier implementation
struct RoundedCornerWithShadowModifier: ViewModifier {

    let contentPadding: CGFloat
    let cornerRadius: CGFloat
    let isShadow: Bool
    let borderColor: Color
    let borderWidth: CGFloat

    init(contentPadding: CGFloat = 12.0, cornerRadius: CGFloat, isShadow: Bool = false, borderColor: Color, borderWidth: CGFloat) {
        self.contentPadding = contentPadding
        self.cornerRadius = cornerRadius
        self.isShadow = isShadow
        self.borderColor = borderColor
        self.borderWidth = borderWidth
    }

    func body(content: Content) -> some View {
        content
            .padding(contentPadding)
            .background(
                RoundedRectangle(cornerRadius: cornerRadius)
                    .fill(Color.green)
                    .if(isShadow) { $0.shadow(color: Color.black.opacity(0.5), radius: 12, x: 0, y: 0)
                    }
            )
            .overlay(
                RoundedRectangle(cornerRadius: cornerRadius)
                    .stroke(Color.red, lineWidth: borderWidth)
            )
    }
}

// Rounded corner with shadow demo code
struct RoundedCornerWithShadow: View {
    var body: some View {
        Text("Hello World")
            .modifier(RoundedCornerWithShadowModifier(cornerRadius: 12.0, isShadow: true, borderColor: .red, borderWidth: 1.0))
    }
}

In the above example, the view modifier is providing default values for content padding and shadow availability. The client needs to pass the rest of the parameters while applying the view modifier to the input view.

Using the above view modifier, we can apply these styles to any view with just one line of code.

View with rounded corners, border and shadow

As you can see here, even though we changed the way we applied these styles to the view, the implementation logic still remained the same and there is no outward change in the modified view.

Summary

So this was all about styling SwiftUI view with the most frequently used styles in the iOS app. Are there any other styles you want to apply but don't know how? Do you want to suggest any modifications to the code above? What other SwiftUI features would you like to know in depth? Let me know your thoughts by reaching out on LinkedIn.

Source Code

The full source code for this tutorial is available on GitHub Gist for further reference

Support and Feedback

If you have any comments or questions, please feel free to reach out to me on LinkedIn.

If you like my blog content and wish to keep me going, please consider donating on Buy Me a Coffee or Patreon. Help, in any form or amount, is highly appreciated and it's a big motivation to keep me writing more articles like this.

Consulting Services

I also provide a few consulting services on Topmate.io, and you can reach out to me there too. These services include,

  1. Let's Connect
  2. Resume Review
  3. 1:1 Mentorship
  4. Interview Preparation & Tips
  5. Conference Speaking
  6. Take-home Exercise Help (iOS)
  7. Career Guidance
  8. Mock Interview