Show line / separator view in SwiftUI

I want to show a separator line in my SwiftUI app. To achieve that, I tried to create an empty view with a fixed frame and a background color / border:

EmptyView()
    .frame(width: 200, height: 2)
    .background(Color.black) // or:
    .border(Color.black, width: 2)

Unfortunately, I cannot see any dark view showing up.
Is there a way to show a separator / line view?


Solution 1:

Use Divider:

A visual element that can be used to separate other content.

Example:

struct ContentView : View {
    var body: some View {
        VStack {
            Text("Hello World")
            Divider()
            Text("Hello Another World")
        }
    }
}

Output: enter image description here

Solution 2:

If anyone is interested a divider, text, divider, looking like this:

enter image description here

LabelledDivider code

struct LabelledDivider: View {

    let label: String
    let horizontalPadding: CGFloat
    let color: Color

    init(label: String, horizontalPadding: CGFloat = 20, color: Color = .gray) {
        self.label = label
        self.horizontalPadding = horizontalPadding
        self.color = color
    }

    var body: some View {
        HStack {
            line
            Text(label).foregroundColor(color)
            line
        }
    }

    var line: some View {
        VStack { Divider().background(color) }.padding(horizontalPadding)
    }
}

It's kind of ugly but I had to put the Dividers into a VStack to make them horizontal, otherwise, they will be vertical, due to HStack. Please let me know if you managed to simplify this :)

Also maybe using and stored properties for LabelledDivider might not be the most SwiftUI-y solution, so I'm open to improvements.

Example usage

This is the code that results in the screenshot seen above:

struct GetStartedView: View {
    var body: some View {
        NavigationView {
            VStack {

                NavigationLink(destination: SignInView()) {
                    Text("Sign In").buttonStyleEmerald()
                }

                LabelledDivider(label: "or")

                NavigationLink(destination: SignUpView()) {
                    Text("Sign up").buttonStyleSaphire()
                }

            }.padding(20)
        }
    }
}

ButtonStyle

For sake of completness, I also include buttonStyle view modifiers:

struct ButtonStyle: ViewModifier {

    private let color: Color
    private let enabled: () -> Bool
    init(color: Color, enabled: @escaping () -> Bool = { true }) {
        self.color = color
        self.enabled = enabled
    }

    dynamic func body(content: Content) -> some View {
        content
            .padding()
            .frame(minWidth: 0, maxWidth: .infinity, alignment: .center)
            .foregroundColor(Color.white)
            .background(enabled() ? color : Color.black)
            .cornerRadius(5)
    }
}

extension View {
    dynamic func buttonStyleEmerald(enabled: @escaping () -> Bool = { true }) -> some View {
        ModifiedContent(content: self, modifier: ButtonStyle(color: Color.emerald, enabled: enabled))
    }

    dynamic func buttonStyleSaphire(enabled: @escaping () -> Bool = { true }) -> some View {
        ModifiedContent(content: self, modifier: ButtonStyle(color: Color.saphire, enabled: enabled))
    }

}

Edit: Please note that Color.saphire and Color.emerald are custom declared colors:

extension Color {
    static var emerald:     Color { .rgb(036, 180, 126) }
    static var forest:      Color { .rgb(062, 207, 142) }
}

extension Color {
    static func rgb(_ red: UInt8, _ green: UInt8, _ blue: UInt8) -> Color {
        func value(_ raw: UInt8) -> Double {
            return Double(raw)/Double(255)
        }
        return Color(
            red: value(red),
            green: value(green),
            blue: value(blue)
        )
    }
}

Solution 3:

You can just draw a line by using Color. If you want to change the line width or padding, you can use frame or padding like other SwiftUI Components.

//Horizontal Line in VStack
VStack{
    Color.gray.frame(height:CGFloat(1) / UIScreen.main.scale)
}
//Vertical Line in HStack
HStack{
    Color.gray.frame(width:CGFloat(1) / UIScreen.main.scale)
}

Solution 4:

If you are looking for a way to customize the divider, there isn't any. You must provide your custom implementation:

struct CustomDivider: View {
    let height: CGFloat = 1
    let color: Color = .white
    let opacity: Double = 0.2
    
    var body: some View {
        Group {
            Rectangle()
        }
        .frame(height: height)
        .foregroundColor(color)
        .opacity(opacity)
    }
}