New in SwiftUI 3: New for List in SwiftUI 3 and iOS 15

DevTechie Inc
Apr 6, 2022

Lists are one of the most popular views used in SwiftUI and SwiftUI 3 changes made List view even better.

In this article, we will look at new changes that were brought in for List in SwiftUI 3. Topics will be:

  • List Row Separator
  • List Row Separator Tint Color
  • List Section Separator
  • List Section Separator Tint Color
ListRowSeparator

Before iOS 15, hiding or showing List Row Separator would require you to modify underlying tableView’s appearance properties but with new release of SwiftUI 3, new modifier has been added to expose functionality SwiftyUI 😍way.

To understand practical use cases, we will build page of DevTechie Courses app. The home page will list out courses published by DevTechie (that’s me 😁)

Let’s start with Model:

struct DevTechieCourse: Identifiable {
    var id = UUID()
    var name: String
    var chapters: Int
}
Next we will create sample data for our DevTechie Courses

extension DevTechieCourse {
    static var sampleData: [DevTechieCourse] {
        [
            DevTechieCourse(name: "Machine Learning in iOS", chapters: 10),
            DevTechieCourse(name: "CollectionViews in iOS", chapters: 12),
            DevTechieCourse(name: "SwiftUI DeepDive", chapters: 14),
            DevTechieCourse(name: "Goals App in SwiftUI", chapters: 16),
            DevTechieCourse(name: "Firebase and SwiftUI", chapters: 8),
            DevTechieCourse(name: "Disney Plus Clone in SwiftUI", chapters: 15),
            DevTechieCourse(name: "Strava Clone in UIKit, iOS and Swift", chapters: 11),
            DevTechieCourse(name: "Data Structures and Algorithms in Swift", chapters: 21)
        ]
    }
}
Let’s create viewModel for DevTechieCourse model

struct DevTechieCourseViewModel: Identifiable {
    var dtCourse: DevTechieCourse
    
    var id: UUID {
        dtCourse.id
    }
    
    var name: String {
        dtCourse.name
    }
    
    var chapters: String {
        String(dtCourse.chapters)
    }
}
And a list viewModel for DevTechieCourseHomeView :

class DevTechieCourseListViewModel: ObservableObject {
    @Published var dtCourses = [DevTechieCourseViewModel]()
    
    func getAll() {
        dtCourses = DevTechieCourse.sampleData.map(DevTechieCourseViewModel.init)
    }
}
Finally 😅 we have reached section where we will be creating view for DevTechie courses app:

Let’s start with view for each row:

struct DevTechieCourseCell: View {
    var course: DevTechieCourseViewModel
    
    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            Text(course.name)
                .font(.title2)
                .bold()
            
            Text("There are \(course.chapters) chapters in this course.")
                .font(.subheadline)
                .foregroundColor(.secondary)
        }
    }
}
Next, we will add this to our DevTechieCourseHomeView

struct DevTechieCourseHomeView: View {
    
    @ObservedObject private var vm = DevTechieCourseListViewModel()
    
    var body: some View {
        NavigationView {
            List(vm.dtCourses) { course in
                DevTechieCourseCell(course: course)
            }.task {
                vm.getAll()
            }
            .navigationTitle("DevTechie Courses")
        }
    }
    
}
Our list looks good but notice the separator, what if we want to hide that separator from the list? 🤨

Let’s hide separator using .listRowSeparator modifier.

struct DevTechieCourseHomeView: View {
    
    @ObservedObject private var vm = DevTechieCourseListViewModel()
    
    var body: some View {
        NavigationView {
            List(vm.dtCourses) { course in
                DevTechieCourseCell(course: course)
                    .listRowSeparator(.hidden)
            }.task {
                vm.getAll()
            }
            .navigationTitle("DevTechie Courses")
        }
    }
    
}
This will hide separator from both top and bottom edges but if we only want to hide separator from bottom edge then we can specify edge to hide like this:

struct DevTechieCourseHomeView: View {
    
    @ObservedObject private var vm = DevTechieCourseListViewModel()
    
    var body: some View {
        NavigationView {
            List(vm.dtCourses) { course in
                if course.name.contains("SwiftUI") {
                    DevTechieCourseCell(course: course)
                        .listRowSeparator(.hidden, edges: .bottom)
                } else {
                    DevTechieCourseCell(course: course)
                }
            }.task {
                vm.getAll()
            }
            .navigationTitle("DevTechie Courses")
        }
    }
    
}
This modifier applies on row for list which is why we are applying it on the course cell.

ListRowSeparatorTintColor

Hide 🙈 and show 🐵 is all good but what if we only want to change the color for the separator and not hide it?

I was thinking about the same, I must say “our brainwaves are in sync mate!!!” 😊

We can use .listRowSeparatorTint modifier to add tint to our separator.

struct DevTechieCourseHomeView: View {
    
    @ObservedObject private var vm = DevTechieCourseListViewModel()
    
    var body: some View {
        NavigationView {
            List(vm.dtCourses) { course in
                if course.name.contains("SwiftUI") {
                    DevTechieCourseCell(course: course)
                        .listRowSeparatorTint(.green)
                } else {
                    DevTechieCourseCell(course: course)
                        .listRowSeparatorTint(.red)
                }
            }.task {
                vm.getAll()
            }
            .navigationTitle("DevTechie Courses")
        }
    }
    
}
Just like listRowSeparator , listRowSeparatorTint has an overload which takes edges into consideration. So if we want to add different color tint for top and bottom edges, we can do that with:

.listRowSeparatorTint(.orange, edges: .bottom)
ListSectionSeparator

While working in listRowSeparators SwiftUI engineers thought

“Why should list row separator have all the fun, let’s roll this fun to list section separator too” 🤣

With that came .listSectionSeparator modifier, which is applied on Sections(as the name suggests 😉).

Let’s first split our courses into sections, we will have one section for SwiftUI(because it deserves it) and another section for all others.

struct DevTechieCourseHomeView: View {
    
    @ObservedObject private var vm = DevTechieCourseListViewModel()
    
    var body: some View {
        NavigationView {
            List {
                Section(header: Text("SwiftUI")) {
                    ForEach(vm.dtCourses.filter { $0.name.contains("SwiftUI")}) { course in
                        DevTechieCourseCell(course: course)
                    }
                }
                
                Section(header: Text("Others")) {
                    ForEach(vm.dtCourses.filter { !$0.name.contains("SwiftUI")}) { course in
                        DevTechieCourseCell(course: course)
                    }
                }
            }
            .listStyle(.plain)
            .task {
                vm.getAll()
            }
            .navigationTitle("DevTechie Courses")
        }
    }
    
}
Let’s apply .listSectionSeparator modifier to the section.

struct DevTechieCourseHomeView: View {
    
    @ObservedObject private var vm = DevTechieCourseListViewModel()
    
    var body: some View {
        NavigationView {
            List {
                Section(header: Text("SwiftUI")) {
                    ForEach(vm.dtCourses.filter { $0.name.contains("SwiftUI")}) { course in
                        DevTechieCourseCell(course: course)
                    }
                }.listSectionSeparator(.hidden)
                
                Section(header: Text("Others")) {
                    ForEach(vm.dtCourses.filter { !$0.name.contains("SwiftUI")}) { course in
                        DevTechieCourseCell(course: course)
                    }
                }
            }
            .listStyle(.plain)
            .task {
                vm.getAll()
            }
            .navigationTitle("DevTechie Courses")
        }
    }
    
}
Notice the separator line above section header “Others” is no longer visible.👆

ListSectionSeparatorTint

AND just like listRowSeparators we can set tint color to.listSectionSeparator with .listSectionSeparatorTintmodifier.

struct DevTechieCourseHomeView: View {
    
    @ObservedObject private var vm = DevTechieCourseListViewModel()
    
    var body: some View {
        NavigationView {
            List {
                Section(header: Text("SwiftUI")) {
                    ForEach(vm.dtCourses.filter { $0.name.contains("SwiftUI")}) { course in
                        DevTechieCourseCell(course: course)
                    }
                }.listSectionSeparatorTint(.red)
                
                Section(header: Text("Others")) {
                    ForEach(vm.dtCourses.filter { !$0.name.contains("SwiftUI")}) { course in
                        DevTechieCourseCell(course: course)
                    }
                }
            }
            .listStyle(.plain)
            .task {
                vm.getAll()
            }
            .navigationTitle("DevTechie Courses")
        }
    }
    
}
With that, we have reached the end of this article. Thank you once again for reading, if you liked it, don’t forget to subscribe our newsletter.