Schiopu Evgheni
Schiopu Evgheni

Reputation: 559

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

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.

Upvotes: 51

Views: 22385

Answers (8)

wangrui460
wangrui460

Reputation: 79

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

Upvotes: 1

Mojtaba Hosseini
Mojtaba Hosseini

Reputation: 119128

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

Upvotes: 32

Fortran programmer
Fortran programmer

Reputation: 77

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.

Upvotes: 5

sam-w
sam-w

Reputation: 7687

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

Upvotes: 5

Masanao Imai
Masanao Imai

Reputation: 125

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.

Upvotes: 6

Matteo Pacini
Matteo Pacini

Reputation: 22846

iOS 13 builds only:

The current workaround is to remove them via UIAppearance:

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

Upvotes: 6

Moumen Alisawe
Moumen Alisawe

Reputation: 2498

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

Upvotes: 14

Rico Crescenzio
Rico Crescenzio

Reputation: 4226

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

Upvotes: 10

Related Questions