So the question is pretty simple and it's in the title. I want to remove the line separator in SwiftUI iOS 14. Previously, I was using
UITableView().appearance().separatorStyle = .none
and that used to do the job in iOS 13. Now however, it doesn't work. Any update or idea on how to make it work. Thanks:)

- 95,414
- 31
- 268
- 278

- 1,830
- 5
- 16
- 34
-
4Well, there is no more `UITableView` underneath... no more hucks )) – Asperi Jun 26 '20 at 16:04
-
3@SchmidtyApps posted a solution that I've confirmed works! But the answer was deleted by a moderator and I can't undelete it. See https://github.com/SchmidtyApps/SwiftUIListSeparator – Jordan H Sep 21 '20 at 16:54
-
2@Asperi what do you mean there is no more UITableView underneath - there is one still – Petar Dec 06 '21 at 18:40
12 Answers
Here is a demo of possible solution. Tested with Xcode 12b.
List {
ForEach(0..<3) { _ in
VStack {
Text("Hello, World!").padding(.leading)
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.listRowInsets(EdgeInsets())
.background(Color(UIColor.systemBackground)))
}
}

- 1,048,767
- 296
- 4,058
- 3,343

- 228,894
- 20
- 464
- 690
-
4
-
3In addition, you may want to apply negative edge insets in some cases. `.listRowInsets(EdgeInsets(.init(top: -1, leading: -1, bottom: -1, trailing: -1)))`. – zrfrank Aug 16 '20 at 18:55
-
2How can you do this without modifying the insets? Removing all the padding makes the list look bad. – Jordan H Aug 23 '20 at 18:22
-
@JordanH You can set the list style to `.listStyle(InsetListStyle())` – pawello2222 Oct 20 '20 at 22:14
-
love this solution and it's working beautifully on simulator (XCode 12, running iOS 13 and 14) however, it doesn't work on my actual device (iPhone XS, iOS 14.2). The list separators are all showing up for some strange reason. Anyone else faced the same? – Qingwan Kuah Jan 08 '21 at 01:23
-
I use button in my list cell. After adding this the highlighted color has gone from my buttons. – Tulon Jul 05 '21 at 03:22
-
What if you actually need to use .listRowInsets with non-zero values for insets ? – Petar Dec 07 '21 at 15:19
Merged @asperi, @akmin and @zrfrank answer into one thing. In my experience List
is more reliable and efficient than LazyVStack
, so I use still use List
for anything complex requiring reliability.
extension View {
func listRow() -> some View {
self.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.listRowInsets(EdgeInsets(top: -1, leading: -1, bottom: -1, trailing: -1))
.background(Color(.systemBackground))
}
}
List {
Color.red
.listRow()
Color.green
.listRow()
}

- 4,377
- 2
- 25
- 23
-
-
-
On iOS 14 it works like a charm! No LazyVStack but only List + view. Thanks man! I was gettin' mad! – Posa Sep 05 '21 at 10:35
-
I'm horrified that this works for iOS 14 and seems to do nothing on iOS 15, but that's the way it plays out. – Robert Atkins Jan 18 '22 at 10:47
How I made a list that works on both iOS 14 and iOS 13, It shows no separators and extra margins
struct NoButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
}
}
struct ListWithoutSepatorsAndMargins<Content: View>: View {
let content: () -> Content
var body: some View {
if #available(iOS 14.0, *) {
ScrollView {
LazyVStack(spacing: 0) {
self.content()
}
.buttonStyle(NoButtonStyle())
}
} else {
List {
self.content()
}
.listStyle(PlainListStyle())
.buttonStyle(NoButtonStyle())
}
}
}
Sample Usage -
ListWithoutSepatorsAndMargins {
ForEach(0..<5) { _ in
Text("Content")
}
}
in case you've more components in list, wrap them in Group
ListWithoutSepatorsAndMargins {
Group {
self.groupSearchResults()
self.myGroups()
self.exploreGroups()
}
}
}
Hope this helps someone, I wasted a lot of time in such minor thing, Apple is trying to push us hard to use LazyVStack, it seems

- 125
- 1
- 4
-
2this solution is harmful, because LazyVStack works with cells in a completely different manner, than List. You can check CPU load when scrolling those cells. If the cell is something more than Text(“hello test”), you will see peak load while scrolling – Vivienne Fosh Oct 05 '20 at 20:25
-
CPU usage goes up a bit while scrolling but comes back down instantly as things stabilize, which used to happen in UITableView as well, I have pretty heavy cells in my app and very big lists, It has never caused a lag in scrolling or crash for us, we saw no issues so far but use at your own risk! – Harish saini Oct 07 '20 at 12:09
-
-
2@Zapko how do you think, what is the main difference between StackView and TableView in UIKit? The same, that List and LazyVStack have. TableView reuses cells, LazyVStack does not. So when you have more elements that fit the screen, and the layout is more complex than Text("Content") you will get extreme slowness and lags. Therefore providing the solution that is called ListWithoutSeparators{} and making it a LazyVStack inside is super harmful. – Vivienne Fosh Oct 09 '20 at 15:30
-
2by using lazy vstack you lose other List benefits like edit state, swipe to delete gesture etc. – Shengchalover Apr 09 '21 at 07:42
-
I have solved it using List here https://stackoverflow.com/a/64028051/14327378, please check it out if you want List :) – Harish saini Apr 12 '21 at 11:32
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(items, id:\.self) {
Text("Row \($0)")
.listRowSeparator(.hidden)
}
Also you can set each separator to any color by settings listRowSeparatorTintColor
as I mentioned here in this answer:
iOS 14
Follow the answer here

- 95,414
- 31
- 268
- 278
-
Downvoted as this doesn't actually answer the question of how to do it in SwiftUI 2.0 on iOS 14 – Alec. Mar 02 '22 at 14:14
I found this simple solution on the Apple Developer forums. It's working for me on 14.4:
List {
...
}.listStyle(SidebarListStyle())
This seems to add a tiny bit of padding around the edges. If that's a problem for you, you could try some negative padding.

- 1,451
- 13
- 17
Based on average Joe's answer I ended up with the following modifier:
struct ListSeparatorNone: ViewModifier {
var backgroundColor: Color = Color(.systemBackground)
func body(content: Content) -> some View {
content
.listRowInsets(EdgeInsets(top: -1, leading: 0, bottom: 0, trailing: 0))
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.background(backgroundColor)
}
}
The view extension:
extension View {
func listSeparatorNone(backgroundColor: Color = Color(.systemBackground)) -> some View {
self.modifier(ListSeparatorNone(backgroundColor: backgroundColor))
}
}
Usage example:
List {
ForEach(viewModel.countries, id: \.self) { country in
Text(country)
.padding(.leading, 10)
}
.listSeparatorNone()
}

- 837
- 12
- 16
If you don't have a lot of cells, and therefore don't need to rely on a LazyVStack for performance, you can fallback to a ScrollView + VStack:
ScrollView {
VStack {
Row1()
Row2()
Row3()
}
}

- 86,719
- 67
- 308
- 465
You can also call this function at the end of your VStack (that is inner the List).
It will be an overlay on List Seperator on iOS 14 :)
private func hideDefaultListSeperator() -> some View {
Rectangle()
.fill(colorScheme == .light ? Color.white : Color.black)
.frame(maxHeight: 1)
}

- 1
- 1
Update:
I figured out a solution that works on both iOS 13 and iOS 14 and gives a simple list and uses List on both iOS.
struct ListWithoutSepatorsAndMargins<Content>: View where Content: View {
let content: () -> Content
init(@ViewBuilder content: @escaping () -> Content) {
self.content = content
}
var body: some View {
List {
self.content()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.listRowInsets(EdgeInsets())
.background(Color.white)
}
.listStyle(PlainListStyle())
.buttonStyle(NoButtonStyle())
}
}
struct NoButtonStyle: ButtonStyle {
func makeBody(configuration: Self.Configuration) -> some View {
configuration.label
}
and do the following in SceneDelegate.swift to remove default grey selection of cells
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
UITableView.appearance().separatorStyle = .none
UITableView.appearance().allowsSelection = false
.......
and we can use it the following way
ListWithoutSepatorsAndMargins {
ForEach(0..<5) { _ in
Text("Content")
}
}
ListWithoutSepatorsAndMargins {
Group {
self.groupSearchResults()
self.myGroups()
self.exploreGroups()
}
}
}

- 125
- 1
- 4
-
-
1add it on the launch of your app, the first piece of code that gets executed – Harish saini Mar 24 '21 at 12:48
-
in later versions of ios I am getting a default spacing between cells which can be removed by doing the following – Harish saini Mar 24 '21 at 12:50
-
`List { self.content() .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) .listRowInsets(EdgeInsets()) .background(Color.white) } .listStyle(PlainListStyle()) .buttonStyle(NoButtonStyle()) .environment(\.defaultMinListRowHeight, 1)` – Harish saini Mar 24 '21 at 12:50
-
the extra `.environment(\.defaultMinListRowHeight, 1)` from the previous solution – Harish saini Mar 24 '21 at 12:52
Here is my solution for iOS 14:
struct MyRowView: View {
var body: some View {
ZStack(alignment: .leading) {
// Background color of the Row. It will spread under the entire row.
Color(.systemBackground)
NavigationLink(destination: Text("Details")) {
EmptyView()
}
.opacity(0) // Hide the Disclosure Indicator
Text("Go to Details").padding(.leading)
}
// These 2 lines hide the row separators
.padding(.horizontal, -16) // Removes default horizontal padding
.padding(.vertical, -6) // Removes default vertical padding
}
}
The enclosing List should have this modifier
.listStyle(PlainListStyle())
The upside of this solution over using a LazyVStack is that you can still use the Edit capabilities of the List.
This solution relies unfortunately on hard-coded values to remove the system default paddings on each row. Hopefully SwiftUI 3.0 will provide simple .separatorStyle(.none) and .accessoryType(.none) modifiers.
The code to remove the Disclosure Indicators comes from: https://www.appcoda.com/hide-disclosure-indicator-swiftui-list/

- 903
- 10
- 17
Thank for @asperi, @akmin and @zrfrank and @averageJoe 's answers.
Here is another improved method works in iOS 14 and 15.
extension View {
func hideListRowSeperator() -> some View {
if #available(iOS 15, *) {
return AnyView(self.listRowSeparator(.hidden))
} else {
return AnyView(self.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
.listRowInsets(EdgeInsets(top: -1, leading: -1, bottom: -1, trailing: -1))
.background(Color(.systemBackground)))
}
}
}
Use example
var body: some View {
List {
ForEach(0..<3) { _ in
Text("Hello, World!")
.padding(.leading)
.hideListRowSeperator()
}
}
.listStyle(.plain)
}

- 1,492
- 14
- 20
The above answer work for me, you have to set only below both function:
.listRowInsets(EdgeInsets())
.background(Color.white)

- 868
- 1
- 12
- 28

- 1