How to Implement Lists in SwiftUI
List in SwiftUI allows us to present rows of data arranged in single column with an option to select one or more row items. In this tutorial we will dive into how to create a simple multi-section List. Here is how to do it:
Creating a Datasource
We will start with creating a datasource which we will use to populate the list with. We will create a struct called Place
and then create another struct called Continent
. We will then create an array of type Place
inside the Continent
struct and use that as our data source. Make sure that Place
struct conforms to Identifiable
protocol. More on this protocol later.
Here is the code for it:
struct Place: Identifiable {
var id = UUID()
var name : String = ""
}
struct DataSource {
static var continent = [Place(name: "Asia"),
Place(name: "Africa"),
Place(name: "South America"),
Place(name: "North America"),
Place(name: "Australia"),
Place(name: "Antartica"),
Place(name: "Europe")]
}
Implementing a Simple List in SwiftUI
Next, we will implement the List, so in the `contentView` please write the following:
struct ContentView: View {
var body: some View {
List(DataSource.continent) { continent in
Text(continent.name)
}
}
}
That's about it! List gets the data source, and returns the item, which we will add in a Text
view.
Adding Sections
Now, lets add sections to this list. We will make these continents as section headers and list countries under each continent (except for Antartica ๐ฅถ). Our datasource will look something like this now:
struct Country: Hashable, Identifiable {
var id = UUID()
var countryName : String = ""
}
struct Place: Identifiable {
var id = UUID()
var name : String = ""
var countries = [Country]()
}
struct DataSource {
static var continent = [Place(name: "Asia",
countries: [Country(countryName: "๐น๐ญ Thailand"),Country(countryName: "๐ฏ๐ต Japan"),Country(countryName: "๐ฎ๐ณ India"),Country(countryName: "๐ต๐ฐ Pakistan"),Country(countryName: "๐ป๐ณ Vietnam")]),
Place(name: "Africa",
countries: [Country(countryName: "๐ฟ๐ฆ South Africa"),Country(countryName: "๐ธ๐ฉ Sudan"),Country(countryName: "๐ณ๐ฌ Nigeria"),Country(countryName: "๐ณ๐ฆ Namibia"),Country(countryName: "๐ฐ๐ช Kenya")]),
Place(name: "South America",
countries: [Country(countryName: "๐ง๐ท Brazil"),Country(countryName: "๐จ๐ด Colombia"),Country(countryName: "๐จ๐ฑ Chile"),Country(countryName: "๐ฆ๐ท Argentina"),Country(countryName: "๐ง๐ด Bolivia")]),
Place(name: "North America",
countries: [Country(countryName: "๐จ๐ฆ Canada"),Country(countryName: "๐บ๐ธ United States of America")]),
Place(name: "Europe",
countries: [Country(countryName: "๐ซ๐ท France"),Country(countryName: "๐ฉ๐ช Germany"),Country(countryName: "๐จ๐ญSwitzerland"),Country(countryName: "๐ฌ๐ง United Kingdom"),Country(countryName: "๐ณ๐ฑ Netherland")]),
Place(name: "Oceania",
countries: [Country(countryName: "๐ฆ๐บ Australia"),Country(countryName: "๐ณ๐ฟ New Zealand"),Country(countryName: "๐ต๐ฌ Papua New Guinea")]),
Place(name: "Antartica",
countries: [Country(countryName: "๐ฅถ")])
]
}
We have a new struct called Country
that conforms to Hashable
and Identifiable
and in Place
struct we have an array of type Country
. Going back to the content view will now add few things, here is the code:
NavigationView {
List {
ForEach(DataSource.continent, id: \.id) { continent in
Section(header: Text(continent.name)) {
ForEach(continent.countries, id: \.id) { country in
Text(country.countryName)
}
}
}
}.navigationTitle("๐ World")
}
First we will wrap our List View with a NavigationView
. Inside the List
closure, we will run a ForEach
loop on the continents array. Please note here that we are providing an id
argument as well here. This is used in order to tell the List
how to differentiate between elements. The first ForEach closure returns the element of type Continent
. We will create the section header using the Section
view. We will provide the Continent
name as the header and then inside the content closure we will run another ForEach
loop but this time on continent.countries
array. Note the \.id
, in order to identify each country element. This is the reason why our structs conform to Identifiable
protocols, so that it is mandatory for us to add the id
property for each element. Next, we are just showing the countryName
in the Text view.
Implementing Multi-Section
The last thing I would like to implement is the ability to select multiple items from the list. In order to do this just add the following property inside the content view:
@State private var selection = Set<UUID>()
Next, bind this selection property to the List's selection
argument and then add an EditButton()
to the top navigation bar like this:
NavigationView {
List(selection: $selection) {
....
}.navigationTitle("๐ World")
.toolbar {
EditButton()
}
}
So, now you have an edit button on the navigation bar of the app. Tapping it will let you make multiple selections and all those selections will be stored in the selection
set.
The full code for this project is available on GitHub.