71

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?

L A
  • 966
  • 11
  • 25
LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174

6 Answers6

123

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

J. Doe
  • 12,159
  • 9
  • 60
  • 114
85

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)
        )
    }
}
Sajjon
  • 8,938
  • 5
  • 60
  • 94
  • What is `Color.Radix.emerald`? – gotnull Jan 05 '20 at 08:52
  • Just some color I have declared... feel free to replace with your own :) – Sajjon Jan 05 '20 at 10:03
  • @Sajjon I know this is late but you can remove the init and just define the label in the view constructor in similar fashion to if you used the init. Just removed a layer of complexity. – Harry J Apr 26 '20 at 07:23
  • I use very similar code to this and had to add `.lineLimit(1).fixedSize()` to prevent line-breaks and ellipses for longer translations – Jannik Arndt Jun 18 '21 at 11:53
  • Inherit color separator light and dark theme, color: Color = Color(UIColor.separator) – Codelaby Jun 27 '23 at 14:16
35

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: 1 / UIScreen.main.scale)
}
//Vertical Line in HStack
HStack{
    Color.gray.frame(width: 1 / UIScreen.main.scale)
}
Islam
  • 3,654
  • 3
  • 30
  • 40
user2027712
  • 379
  • 4
  • 9
3

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)
    }
}
Alexandru Motoc
  • 582
  • 7
  • 14
0

Variant of response @Sajjon

Fixeds:

  • Default UI Color separator for divisor
  • Truncate text
  • correct spacing
  • line ajusting into center of text
struct LabelledDivider: View {

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

    init(label: String, horizontalPadding: CGFloat = 8, color: Color = Color(UIColor.separator)) {
        self.label = label
        self.horizontalPadding = horizontalPadding
        self.color = color
    }

    var body: some View {
        HStack(alignment: .center, spacing: 0) {
            line
            Text(label)
                .font(.callout)
                .foregroundColor(color)
                .lineLimit(1)
                .fixedSize()
                .offset(y: -1)
            line
        }
    }

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

struct PositionSampleView: View {
    var body: some View {

        LabelledDivider(label: "or")

        
    }
    
}

enter image description here

Codelaby
  • 2,604
  • 1
  • 25
  • 25
-1
HStack {
    VStack {
        Divider()
    }
    Text("or")
        .font(.caption)
        .foregroundColor(Color(UIColor.systemGray))
    VStack {
        Divider()
    }
}
  • Why answering a topic which is already done 3 years ago!? – Iskandir Feb 09 '23 at 13:49
  • Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? – Jeremy Caney Feb 11 '23 at 00:22