50

I'm trying to remove the "row" separators (known as dividers in SwiftUI) from a List in SwiftUI.

I went through the List documentation, but I haven't been able to find a modifier for that.

Any help would be appreciated.

pawello2222
  • 46,897
  • 22
  • 145
  • 209
Schiopu Evgheni
  • 549
  • 1
  • 4
  • 13
  • 1
    Simple rewording to make the post more understandable – Kuya Jun 07 '19 at 08:09
  • Kuya's edit makes sense. I'm having the same issue which is how my search engine lead me here. The original author wants to remove the dividing lines that come by default with the `List` view. – wulong Jun 07 '19 at 17:38
  • @Schiopu Evgheni please mark an answer as accepted and don't let a question be open for years. Thanks – Mojtaba Hosseini Aug 03 '21 at 05:28

8 Answers8

30

iOS 15:

This year Apple introduced a new modifier .listRowSeparator that can be used to style the separators. you can pass .hidden to hide it:

List {
    ForEach(items, id:\.self) { 
        Text("Row \($0)")
            .listRowSeparator(.hidden)
    }
}

iOS 14

Apple introduced LazyVStack In iOS 14. you may consider using it instead of list for this:

ScrollView {
    LazyVStack {
        ForEach((1...100), id: \.self) {
           Text("Placeholder \($0)")
        }
    }
}

Keep in mind that LazyVStack is lazy and doesn't render all rows all the time. So they are very performant and suggested by Apple itself in WWDC 2020.


iOS 13

There is a UITableView behind SwiftUI's List for iOS. So to remove

Extra separators (below the list):

you need a tableFooterView and to remove

All separators (including the actual ones):

you need separatorStyle to be .none

init() {
    // To remove only extra separators below the list:
    UITableView.appearance().tableFooterView = UIView()

    // To remove all separators including the actual ones:
    UITableView.appearance().separatorStyle = .none
}

var body: some View {
    List {
        Text("Item 1")
        Text("Item 2")
        Text("Item 3")
    }
}
Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
  • 1
    I tried your suggestion for iOS 14 with Xcode 12.0 beta 2 (ScrollView / LazyVStack), but it still shows the cell separators. Additionally, List still has the same separators. – Reinhard Männer Jul 15 '20 at 12:08
  • 2
    Replacing List with Scrollview with VStack messes with the NavigationView with large title – Adrian Ciolea Aug 06 '20 at 14:27
  • ScrollView { LazyVStack { ForEach((1...100), id: \.self) { Text("Placeholder \($0)") } } } Its nicely done my job on iOS 14 – XpressGeek Dec 03 '21 at 15:13
14

iOS 13 builds only

while this solution works correctly let's clean up the work using ViewModifier

public struct ListSeparatorStyleNoneModifier: ViewModifier {
    public func body(content: Content) -> some View {
        content.onAppear {
            UITableView.appearance().separatorStyle = .none
        }.onDisappear {
            UITableView.appearance().separatorStyle = .singleLine
        }
    }
}

now let's make a small extension that would help to hide the details

extension View {
    public func listSeparatorStyleNone() -> some View {
        modifier(ListSeparatorStyleNoneModifier())
    }
}

As you can see, we’ve wrapped our appearance setting code into a neat little view modifier. you can declare it directly now

List {
    Text("1")
    Text("2")
    Text("3")
}.listSeparatorStyleNone()
sam-w
  • 7,478
  • 1
  • 47
  • 77
Moumen Alisawe
  • 2,498
  • 2
  • 12
  • 18
10

You may use ForEach within a ScrollView instead of List for dynamic views without any styling

Rico Crescenzio
  • 3,952
  • 1
  • 14
  • 28
6

iOS 13 builds only:

Adding UITableView.appearance().separatorColor = .clear for initializer

struct SomeView: View {
  init() {
        UITableView.appearance().separatorColor = .clear
    }
}

I hope you to resolve this problem.

sam-w
  • 7,478
  • 1
  • 47
  • 77
Masanao Imai
  • 125
  • 2
  • 2
6

iOS 13 builds only:

The current workaround is to remove them via UIAppearance:

UITableView.appearance(whenContainedInInstancesOf: 
    [UIHostingController<ContentView>.self]
).separatorStyle = .none
sam-w
  • 7,478
  • 1
  • 47
  • 77
Matteo Pacini
  • 21,796
  • 7
  • 67
  • 74
5

iOS 13 builds only:

See existing answers using UITableView.appearance().

⚠️ Be aware that in the iOS 14 SDK, List does not appear to be backed by UITableView. See the alternate solution below:

iOS 14 Xcode 12 Beta 1 only:

I do have a pure SwiftUI solution for iOS 14, but who knows how long it's going to continue working for. It relies on your content being the same size (or larger) than the default list row and having an opaque background.

⚠️ This does not work for iOS 13 builds.

Tested in Xcode 12 beta 1:

yourRowContent
  .padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16))
  .frame(
    minWidth: 0, maxWidth: .infinity,
    minHeight: 44,
    alignment: .leading
  )
  .listRowInsets(EdgeInsets())
  .background(Color.white)

Or if you're looking for a reusable ViewModifier:

import SwiftUI

struct HideRowSeparatorModifier: ViewModifier {

  static let defaultListRowHeight: CGFloat = 44

  var insets: EdgeInsets
  var background: Color

  init(insets: EdgeInsets, background: Color) {
    self.insets = insets

    var alpha: CGFloat = 0
    UIColor(background).getWhite(nil, alpha: &alpha)
    assert(alpha == 1, "Setting background to a non-opaque color will result in separators remaining visible.")
    self.background = background
  }

  func body(content: Content) -> some View {
    content
      .padding(insets)
      .frame(
        minWidth: 0, maxWidth: .infinity,
        minHeight: Self.defaultListRowHeight,
        alignment: .leading
      )
      .listRowInsets(EdgeInsets())
      .background(background)
  }
}

extension EdgeInsets {

  static let defaultListRowInsets = Self(top: 0, leading: 16, bottom: 0, trailing: 16)
}

extension View {

  func hideRowSeparator(
    insets: EdgeInsets = .defaultListRowInsets,
    background: Color = .white
  ) -> some View {
    modifier(HideRowSeparatorModifier(
      insets: insets,
      background: background
    ))
  }
}

struct HideRowSeparator_Previews: PreviewProvider {

  static var previews: some View {
    List {
      ForEach(0..<10) { _ in
        Text("Text")
          .hideRowSeparator()
      }
    }
    .previewLayout(.sizeThatFits)
  }
}
sam-w
  • 7,478
  • 1
  • 47
  • 77
  • This is correct answer for iOS 14. For iOS 13 builds answers with UITableView.appearance().separatorStyle = .none work fine. – BurgerZ Jun 30 '20 at 12:45
  • 2
    It's not **removing** the separator. It is just adding a `.background(Color.white)` on a view on top of the separator and blocks the view. try `.background(Color.red.opacity(0.2))` to see yourself. – Mojtaba Hosseini Jun 30 '20 at 13:47
  • 1
    @MojtabaHosseini if you check out the code I posted, you'll see that I'm asserting exactly the same thing! Thanks so much for being so specific about the definition of `removing`! – sam-w Jul 01 '20 at 01:37
  • 1
    this solution still shows the separator on top of the first row in list – JAHelia Sep 30 '20 at 06:23
  • This is asinine. Not your code - the NEED for that code. SwiftUI continues to be not ready for prime time. :( – ChrisH Feb 23 '21 at 20:01
  • Apple really rushed SwiftUI by at least 2 years. So much stuff missing – Karanveer Singh Jun 20 '21 at 07:01
  • This works, but it's more like a "rowBackground" that covers the separator (and covers the `listRowBackground` if set). To handle anything over than a white row, I'd recommend renaming the extension to `rowBackground(color: _)` to allow setting the color. If you need to add a separator, you can then add it to view itself. – Aaron Hayman Jul 09 '21 at 12:24
5

From: Swiftui Views Mastery Book SwiftUI 2.0 Mark Moeykens

.listStyle(SidebarListStyle()) # IOS 14

You can apply this new list style which will remove the separator lines.

Cristik
  • 30,989
  • 25
  • 91
  • 127
1

For iOS13,iOS14,iOS15,and remove the separator at the top of the first cell

Add viewModifier

extension View {
    /// 隐藏 List 中的 分割线
    func hideRowSeparator(insets: EdgeInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0),
                          background: Color = .white) -> some View {
        modifier(HideRowSeparatorModifier(insets: insets, background: background))
    }
}

struct HideRowSeparatorModifier: ViewModifier {

  static let defaultListRowHeight: CGFloat = 44

  var insets: EdgeInsets
  var background: Color

  init(insets: EdgeInsets, background: Color) {
    self.insets = insets

    var alpha: CGFloat = 0
    if #available(iOS 14.0, *) {
        UIColor(background).getWhite(nil, alpha: &alpha)
        assert(alpha == 1, "Setting background to a non-opaque color will result in separators remaining visible.")
    }
    self.background = background
  }

  func body(content: Content) -> some View {
    content
        .padding(insets)
        .frame(minWidth: 0, maxWidth: .infinity, minHeight: Self.defaultListRowHeight)
        .listRowInsets(EdgeInsets())
        .overlay(
            VStack {
                HStack {}
                .frame(maxWidth: .infinity)
                .frame(height: 1)
                .background(background)
                Spacer()
                HStack {}
                .frame(maxWidth: .infinity)
                .frame(height: 1)
                .background(background)
            }
            .padding(.top, -1)
        )
  }
}

Usage

struct ContentView: View {
    var body: some View {
        List {
            ForEach(0 ..< 30) { item in
                HStack(alignment: .center, spacing: 30) {
                    Text("Hello, world!:\(item)").padding()
                }
                .hideRowSeparator(background: .white)
            }
        }
        .listStyle(PlainListStyle())
    }
}

You can find here

https://github.com/wangrui460/HiddenListLine4SwiftUI

iOS / SwiftUI 技术交流

我创建了一个 微信 iOS 技术交流群、SwiftUI 技术交流群,欢迎小伙伴们加入一起交流学习~

可以加我微信我拉你进去(备注iOS),我的微信号 wr1204607318

wangrui460
  • 79
  • 5