Borders

It seems a little odd to write a post about borders this late, since every post so far has already used them without calling them out explicitly. In each of the examples I’ve added borders to the code given to better illustrate the layout.

It’s worth spending a little time looking at them in their own right though, because they’re slightly more interesting than you might expect.

There is a single method for specifying the border for a view:

func border<S>(_ content: S, width: CGFloat = 1) -> some View where S : ShapeStyle

The first parameter is required and specifies a shape style, there’s a quite a few options for that, but fortunately Color confirms to the ShapeStyle protocol so for the simplest cases all we need to do is specify a color.

The second parameter is optional and specifies the width of the border, defaulting to a single pixel.

As with most of SwiftUI, this is intuitive enough that add a single pixel yellow border on Text we would use code like this:

struct ContentView : View {
    var body: some View {
        Text("Nogitsune Takeshi")
            .font(.title)
            .border(Color.yellow)
    }
}

And this produces the same example I’ve used before, except with the border that’s always been in the preview actually stated in code:

text with a border

The reason that borders are really useful for experimenting with or demonstrating layout is that they don’t work like frame or padding; in that they do not add space around the Text to draw the border.

It’s not obvious with a single pixel, but we can demonstrate this by increasing the width of the border:

struct ContentView : View {
    var body: some View {
        Text("Nogitsune Takeshi")
            .font(.title)
            .border(Color.yellow, width: 4)
    }
}

If this worked like padding, the border would increase in width around the text; instead we see that that border overlays it; .border creates a secondary view on its child, and draws the border overlaid on top of it.

text with a thick border

.border creates a secondary view on its child, and draws the border overlaid on top of it

If we wanted the border around the view instead, we can combine it with .padding:

struct ContentView : View {
    var body: some View {
        Text("Nogitsune Takeshi")
            .font(.title)
            .padding(4)
            .border(Color.yellow, width: 4)
    }
}

This creates the Text view, and then .padding creates another view around that with additional padding added, and then .border adds a secondary view to the padding view, and draws overlaid on that:

text with thick border and padding

It’s important to note the distinction that the border is on the padding view; combined effects can be performed by carefully placing the overlays in the correct place:

struct ContentView : View {
    var body: some View {
        Text("Nogitsune Takeshi")
            .font(.title)
            .border(Color.red)
            .padding(4)
            .border(Color.yellow, width: 4)
            .border(Color.red)
    }
}

Here we create a red border overlaid on the Text, and then use padding to draw a thicker yellow border around the Text, and finally overlaid another red border onto the padding:

text with three borders

The total border width is 5px since it includes the additional pixel-wide border overlaid on the Text, or put another way, the yellow part of the border is 3px wide since the outer pixel is overlaid by the red border added to it.