Remove List Separator in SwiftUI (All Versions)
Reading time: 2 min
This recipe shows how to remove / hide list separator in SwiftUI. This is tricky because each SwiftUI version has its own solution.
The end result looks like this:
This recipe will first show the solutions for each SwiftUI version and then present a custom component that works on any version.
SwiftUI 1 (iOS 13, MacOS 10.15)
In SwiftUI 1, you need to tap into the underlying UITableView
and change its appearance
to hide the separator:
List(1...100, id: \.self) { item in
Text("\(item)")
}.onAppear {
UITableView.appearance().separatorStyle = .none
}.onDisappear {
UITableView.appearance().separatorStyle = .singleLine
}
SwiftUI 2 (iOS 14, MacOS 11)
SwiftUI 2 is the troublemaker of the bunch, because SwiftUI 1 solution just doesn't work on it (which is weird by itself). Unfortunately, there's no easy way out here: you need to simulate a List
with a ForEach
within a LazyVStack
inside a ScrollView
. Yeah, it's that bad.
ScrollView {
LazyVStack {
ForEach(1...100, id: \.self) { item in
Text("\(item)")
}
}
}
The biggest issue with this approach is that it just doesn't look like a normal list. You can modify the views a bit to achieve the same effect, but it's just sad having to spend time on frivolous issues like that.
SwiftUI 3+ (iOS 15+, MacOS 12+)
SwiftUI 3 finally properly solves this problem with a new modifier, listRowSeparator
, that you can attach to row content views:
List(1...100, id: \.self) { item in
Text("\(item)")
.listRowSeparator(.hidden)
}
Solution for any version
Here's a small custom view that merges all the recipes above into a single, reusable component:
struct NoSeparatorList<Data, ID, Content>: View where Data: RandomAccessCollection, ID: Hashable, Content: View {
let data: Data
let id: KeyPath<Data.Element, ID>
let content: (Data.Element) -> Content
public init(_ data: Data,
id: KeyPath<Data.Element, ID>,
@ViewBuilder content: @escaping (Data.Element) -> Content) {
self.data = data
self.id = id
self.content = content
}
var body: some View {
if #available(iOS 15.0, *) {
List(data, id: id) { item in
content(item)
.listRowSeparator(.hidden)
}
} else if #available(iOS 14.0, *) {
ScrollView {
LazyVStack {
ForEach(data, id: id, content: content)
}
}
} else {
List(data, id: id, rowContent: content)
.onAppear {
UITableView.appearance().separatorStyle = .none
}.onDisappear {
UITableView.appearance().separatorStyle = .singleLine
}
}
}
}
Then, you can simply use it like this:
NoSeparatorList(1...100, id: \.self) { index in
HStack {
Text("List item \(index)")
Spacer()
Image(systemName: "chevron.right")
}
}