How to remove "row" separators/dividers from a List in SwiftUI?

17,253

Solution 1

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")
    }
}

Solution 2

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()

Solution 3

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

Solution 4

iOS 13 builds only:

The current workaround is to remove them via UIAppearance:

UITableView.appearance(whenContainedInInstancesOf: 
    [UIHostingController<ContentView>.self]
).separatorStyle = .none

Solution 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)
  }
}
Share:
17,253

Related videos on Youtube

Schiopu Evgheni
Author by

Schiopu Evgheni

(Your about me is currently blank.)

Updated on July 30, 2021

Comments

  • Schiopu Evgheni
    Schiopu Evgheni almost 3 years

    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.

    • Kuya
      Kuya about 5 years
      Simple rewording to make the post more understandable
    • VdeVentura
      VdeVentura about 5 years
      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.
    • Mojtaba Hosseini
      Mojtaba Hosseini almost 3 years
      @Schiopu Evgheni please mark an answer as accepted and don't let a question be open for years. Thanks
  • zerohedge
    zerohedge about 5 years
    This doesn't work if the ScrollView is inside a VStack
  • elight
    elight over 4 years
    Great answer, gonna run with this!
  • Thanh-Nhon Nguyen
    Thanh-Nhon Nguyen about 4 years
    This solution doesn't work when you have a 2nd list presented as sheet and you don't want to hide separator in that 2nd list.
  • BurgerZ
    BurgerZ about 4 years
    This is correct answer for iOS 14. For iOS 13 builds answers with UITableView.appearance().separatorStyle = .none work fine.
  • Mojtaba Hosseini
    Mojtaba Hosseini about 4 years
    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.
  • sam-w
    sam-w about 4 years
    @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!
  • Reinhard Männer
    Reinhard Männer almost 4 years
    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.
  • Adrian Ciolea
    Adrian Ciolea almost 4 years
    Replacing List with Scrollview with VStack messes with the NavigationView with large title
  • JAHelia
    JAHelia almost 4 years
    this solution still shows the separator on top of the first row in list
  • Wahab Khan Jadon
    Wahab Khan Jadon over 3 years
    Worked on iOS14 but how to hide > this simbol at the end of row ...
  • ChrisH
    ChrisH over 3 years
    This is asinine. Not your code - the NEED for that code. SwiftUI continues to be not ready for prime time. :(
  • ScottyBlades
    ScottyBlades about 3 years
    This doesn't lazy display like a list either.
  • Rico Crescenzio
    Rico Crescenzio about 3 years
    Use a LazyVStack for lazy loading
  • Karanveer Singh
    Karanveer Singh about 3 years
    Apple really rushed SwiftUI by at least 2 years. So much stuff missing
  • Aaron Hayman
    Aaron Hayman almost 3 years
    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.
  • XpressGeek
    XpressGeek over 2 years
    ScrollView { LazyVStack { ForEach((1...100), id: \.self) { Text("Placeholder ($0)") } } } Its nicely done my job on iOS 14
  • Logan
    Logan over 2 years
    this should be the accepted answer! Also, I was able to just do .listStyle(.sidebar)