Displaying Borders in SwiftUI

SwiftUI makes it quite easy to create beautiful user interfaces. That fact becomes even bolder when it comes to apply simple styling to views, as many times a single line of code is enough to do the job. Drawing borders around views is such a case, as we can achieve nice results with almost no effort at all. This post demonstrates how to display simple borders, as well as how to create more complicated and configurable borders.

Displaying simple borders

Displaying a simple border around a view is just a matter of calling a view modifier. The following code illustrates that, by adding a border around a text view. Note that specifying the desired color for the border is mandatory:

Text("Borders in SwiftUI!")
    .border(.pink)

Text view with simple border around it.

You can notice that there is no spacing between the text and the border by default. We will rarely want to keep it like that, but it's easy to fix it if we add some padding to the view. This will increase the distance between the view's content and the border:

Text("Borders in SwiftUI!")
    .padding()
    .border(.pink)

Text view with border around it and padding between text view's content and the border.

Most of the times, the order of view modifiers in SwiftUI matters, as different order can lead to different results. This rule applies in this case too, so it's important to add the padding() view modifier before the border.

By default, the width of the border is 1 point. We can change it and set another value by providing an additional argument like so:

Text("Borders in SwiftUI!")
    .padding()
    .border(.pink, width: 5)

Text view with border having line width increased to 5 points.

Not just rectangular borders

The demonstrated border above has a rectangular shape, and we only customized the color and the line width. However, we often need to draw borders with rounded corners, and the above view modifier does not give us any option to set that.

In such cases, there is a different path to take, and instead of a border, we should add an overlay to the view. A RoundedRectangle is the best shape to use in the overlay, given that it allows to set a corner radius value.

But the shape, like the RoundedRectangle, is not a border, nor it draws one automatically. To change that, and make the shape act as a border, all we have to do is to apply a stroke to it, passing as argument the desired color.

All the above are demonstrated in the following code:

Text("Borders in SwiftUI!")
    .padding()
    .overlay(
        RoundedRectangle(cornerRadius: 5)
            .stroke(.pink)
    )

Text view with overlay rounded rectangle shape acting as a border with rounded corners.

Once again, the default line width is 1 point. In order to override this value and set a different one, we supply the stroke view modifier with one more argument; the lineWidth:

Text("Borders in SwiftUI!")
    .padding()
    .overlay(
        RoundedRectangle(cornerRadius: 5)
            .stroke(.pink, lineWidth: 8)
    )

Rounded rectangle shape as border with increased line width.

Obviously we can use any shape in the overlay, not just a RoundedRectangle. The next code demonstrates how to use a Circle instead. Note, however, that for this specific shape, it's also necessary to set a frame to the text view:

Text("Borders in SwiftUI!")
    .frame(width: 150, height: 150)
    .padding()
    .overlay(
        Circle()
            .stroke(.pink, lineWidth: 3)
    )

Circular shape as a border.

Lastly, besides the solid line that the shape has by default, we can also specify a different stroke style; for instance, a dashed line. In the stroke view modifier we replace the lineWidth argument with a StrokeStyle instance as shown next:

Text("Borders in SwiftUI!")
    .frame(width: 150, height: 150)
    .padding()
    .overlay(
        Circle()
            .stroke(.pink,
                    style: StrokeStyle(lineWidth: 3, dash: [20, 5]))
    )

Circular shape with dashed line.

The line width is the first argument in the StrokeStyle initializer. The numbers in the dash argument indicate the length of each painted and unpainted segment in the dashed line. Note that we can provide more arguments to StrokeStyle, but in fact, we will almost never need to use them.

Conclusion

For simple borders around a view, the border view modifier is the easiest way to go. We provide the desired color, and optionally the line width. Remember though to add some padding before the border, so it does not stick to the view's content. For more advanced borders with rounded corners, or even different shapes, such as circle or capsule, the solution is to use an overlay and then the shape we want to display. Feel free to play around a bit with what you just read, and make shapes act as borders the way it fits you the most. Thank you for reading!