List in SwiftUI

swiftui Nov 08, 2023
List in SwiftUI

In SwiftUI, a List is a view that displays a collection of data in a scrollable, vertically stacked format. It is commonly used to create dynamic, scrollable lists of items, such as tables, menus, and other structured data.

You will soon find List very similar to UITableView in UIKit. Let's get familiar with it with some simple demo.

List of items

Here, we list down some simple target for today:

struct ContentView: View {
    var body: some View {
        List {
            Text("Exercise 30 mins")
            Text("Read one Article")
            Text("Research SwiftUI")
        }
    }
}

Result:

Looking great! Now let's add a header and footer to it, just like we used to do with UITableView. To achieve this we will use Section which is a container view that we can use to add hierarchy to certain collection views.

struct ContentView: View {
    var body: some View {
        List {
            Section {
                Text("Exercise 30 mins")
                Text("Read one Article")
                Text("Research SwiftUI")
            } header: {
                Text(Date().formatted(date: .abbreviated, time: .omitted))
            } footer: {
                Text("To do List")
            }
        }
    }
}

Result:

Here we give it a header with the time of today and a simple description footer. Things come out beautiful with just some simple line of codes. In UIKit, we would have use UITableViewHeaderFooterView.

Let's add another section for tommorow:

struct ContentView: View {
    var body: some View {
        List {
            Section {
                Text("Exercise 30 mins")
                Text("Read one Article")
                Text("Research SwiftUI")
            } header: {
                Text(Date().formatted(date: .abbreviated, time: .omitted))
                    .fontWeight(.semibold)
                    .foregroundStyle(.blue)
            } footer: {
                Text("To do List")
            }
            
            Section {
                Text("Make a new friend")
                Text("Study English")
                Text("Research UIKit")
            } header: {
                Text("Tommorow")
                    .fontWeight(.semibold)
                    .foregroundStyle(.blue)
            } footer: {
                Text("Consistence is the key")
            }
        }
    }
}

Result:

We can also add more styles to make our list stand out more. Looking much better! Now let seperate the data to model:

struct TodoItem: Identifiable {
    let id: UUID = .init()
    var title: String
}

Our model will conform to the Identifiable protocol which then help us to idenfify each item to use the ForEach container:

struct ContentView: View {
    
    @State private var todayTodos: [TodoItem] = [
        .init(title: "Exercise 30 mins"),
        .init(title: "Read one Article"),
        .init(title: "Research SwiftUI")
    ]
    
    @State private var tommorowTodos: [TodoItem] = [
        .init(title: "Make a new friend"),
        .init(title: "Study English"),
        .init(title: "Research UIKit")
    ]
    
    var body: some View {
        List {
            Section {
                ForEach(todayTodos, id: \.id) { todo in
                    Text(todo.title)
                }
            } header: {
                Text(Date().formatted(date: .abbreviated, time: .omitted))
                    .fontWeight(.semibold)
                    .foregroundStyle(.blue)
            } footer: {
                Text("To do List")
            }
            
            Section {
                ForEach(tommorowTodos, id: \.id) { todo in
                    Text(todo.title)
                }
            } header: {
                Text("Tommorow")
                    .fontWeight(.semibold)
                    .foregroundStyle(.blue)
            } footer: {
                Text("Consistence is the key")
            }
        }
    }
}

Let's bring some actions to our todo list

Actions with List

Swipe to delete

List comes with some great things available right out of the box. For example like Swipe to delete.

Implement this is very simple and elegance with .onDelete

ForEach(todayTodos, id: \.id) { todo in
    Text(todo.title)
}
.onDelete(perform: { indexSet in
    todayTodos.remove(atOffsets: indexSet)
})

This provide us with the IndexSet tell us the position of swipe item. And Swift also has the build-in method remove(atOffsets _) for it. We have a great to-do list in action:

This used to be troublesome back then with depend on 3rd party solutions. Now things can get much easy and straight forward!

Edit and multiple selections

Let's make our Todo List more complete using NavigationView and adding an EditButton

NavigationView {
	List {
			Section {
			    ForEach(todayTodos, id: \.id) { todo in
			        Text(todo.title)
			    }
			    .onDelete(perform: { indexSet in
			        todayTodos.remove(atOffsets: indexSet)
			    })
			} header: {
				Text(Date().formatted(date: .abbreviated, time: .omitted))
			    	.fontWeight(.semibold)
			      	.foregroundStyle(.blue)
			} footer: {
				Text("To do List")
			}
                
			Section {
				ForEach(tommorowTodos, id: \.id) { todo in
					Text(todo.title)
				}
			} header: {
				Text("Tommorow")
			 		.fontWeight(.semibold)
			 		.foregroundStyle(.blue)
			} footer: {
				Text("Consistence is the key")
			}
		}
       .navigationTitle("Todo List")
       .toolbar(content: {
       	ToolbarItem(placement: .topBarLeading) {
          	EditButton()
         	}
       })
	}
}

Now let's add an options to multiple selections. This again is very simple with native support. First let add a Set of UUID:

 @State private var multiSelection = Set<UUID>()

Then modify our list to List(selection: _):

List(selection: $multiSelection)

We now have a multiple selections list:

All the selected ids will be stored in the multiSelection. You can add an button and do addiotional step with that.

Rearrange Item

Just like with swipe to delete, there is an another very interesting method that help us to easily achive the behavior of kanban board.

Below .onDelete, add new modifier .onMove

ForEach(todayTodos, id: \.id) { todo in
    Text(todo.title)
}
.onDelete(perform: { indexSet in
    todayTodos.remove(atOffsets: indexSet)
})
.onMove(perform: { indices, newOffset in
     todayTodos.move(fromOffsets: indices, toOffset: newOffset)
})

This method provide 2 outputs which will tell us about the old and new positions. Again, Swift has with a very neatly method move(fromOffsets _, toOffset _) for this situation. Now with this code we have a drag drop kanman board behavior:

Refreshing the list content

Developers usually use List to display set of items return from APIs, so pull to refresh is a must have bahavior. With List, we can easily achieve it with refreshable(action: ) modifier. Add this to our List

.refreshable {
	var copyToday = todayTodos
  	var copyTommorow = tommorowTodos
	copyToday.append(.init(title: "Complete all task at work"))
	copyTommorow.append(.init(title: "Draw a flower"))
	todayTodos.removeAll()
	tommorowTodos.removeAll()
		DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
			todayTodos = copyToday
			tommorowTodos = copyTommorow
		}
	}

Here we are mocking a simple API call that will return about 1 section from now with new additional data.

Result:

Styling lists

SwiftUI chooses a display style for a list based on the platform and the view type in which it appears. Use the listStyle(_:) modifier to apply a different ListStyle to all items within a view.

For example, adding .listStyle(PlainListStyle()) to the example, the following screenshot shows:

There are many styles for you to explore, but be aware that these styles will look different on different OS (iOS, macOS, ipadOS and visionOS).

Lastly, according to WWDC2020-10031: List contents are always loaded lazily. This mean that only the cells/rows currently visible on the screen are loaded into memory. When a user scrolls through a list, cells that go off-screen can be dequeued and reused for new content that's about to appear. This reduces memory usage and eliminates the need to create new cells constantly.

How great is that, just like UITableViewCell and UITableView. Performance will be great and optimized.

Beautiful! Now you are a master of List in SwiftUI.

We have launched our new e-book "Cracking the iOS Interview" with Top 100 iOS Interview Questions & Answers. Our book has helped more than 394 iOS developers in successfully cracking their iOS Interviews.

Grab your copy now and rock your next iOS Interview!

Signup now to get notified about our
FREE iOS Workshops!