In the previous SwiftUI tutorial we saw how to create a table or List with SwiftUI. In this tutorial we will see how to create a list view with text and images. In many cases the items in a list view will contain both text and images. How can we create it? If you are familiar with Image, Text, VStack and HStack, you should have some ideas of how to create a list with text and images.
To create a table using UIKit, you need to create a table view or table view controller and then customize the prototype cell. Additionally, you have to program the table view data source to provide the data. There are a few steps to create a table UI. Let’s now see how we can implement the same table view in SwiftUI.
The first thing we need to do is import the images we want to use in our app to the asset catalog.
The first thing we are going to do is declare two arrays of nine elements in ContenView. These arrays will be used to store the restaurant names and images. Here you can see the complete code:
struct ContentView: View {
var body: some View {
var restaurantNames = ["Cafe Deadend", "Homei", "Teakha", "Cafe Loisl", "Petite Oyster", "For Kee Restaurant", "Po's Atelier", "Bourke Street Bakery", "Haigh's Chocolate"]
var restaurantImages = ["cafedeadend", "homei", "teakha", "cafeloisl", "petiteoyster", "forkeerestaurant", "posatelier", "bourkestreetbakery", "haighschocolate"]
}
}
Both arrays have the same number of items. The restaurantNames array stores the restaurant names while the restaurantImages array stores the names of the images we imported to the assets catalog. To create a list with text and images we will have to update the ContentView with the following code:
struct ContentView: View {
var body: some View {
var restaurantNames = ["Cafe Deadend", "Homei", "Teakha", "Cafe Loisl", "Petite Oyster", "For Kee Restaurant", "Po's Atelier", "Bourke Street Bakery", "Haigh's Chocolate"]
var restaurantImages = ["cafedeadend", "homei", "teakha", "cafeloisl", "petiteoyster", "forkeerestaurant", "posatelier", "bourkestreetbakery", "haighschocolate"]
List(restaurantNames.indices, id: \.self) { index in
HStack {
Image(restaurantImages[index])
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(restaurantNames[index])
}
}
.listStyle(.plain)
}
}
We made a few changes to the code. First, instead of a fixed range, we passed the array of restaurant names (i.e. restaurantNames.indices) to the list view. The restaurantNames array has 9 elements, so we will have a range of 0 to 8 (the arrays have an index of 0). This approach works when both arrays are the same size, since the index of one array is used as an index for the other array.
In the closing, the code to create the row layout was updated. To change the style of the list view, we applied the listStyle modifier and set the style to plain.
The result of the executed code is as follows:
Working with data collections
As we mentioned before, List can span a range or set of data. Let’s now see how to use List with an array of restaurant objects.
Instead of holding the restaurant data in two separate arrays, we’ll create a Restaurant struct to organize the data better. This struct has two properties name and image. Insert the following code at the end of ContentView.swift
struct Restaurant{
var name: String
var image: String
}
With this struct we can combine both restaurantNames and restaurantImages into a single array. Delete the restaurantNames and restaurantImages variables and replace them with this variable in ContentView:
var restaurants = [ Restaurant(name: "Cafe Deadend", image: "cafedeadend"),
Restaurant(name: "Homei", image: "homei"),
Restaurant(name: "Teakha", image: "teakha"),
Restaurant(name: "Cafe Loisl", image: "cafeloisl"),
Restaurant(name: "Petite Oyster", image: "petiteoyster"),
Restaurant(name: "For Kee Restaurant", image:
"forkeerestaurant"),
Restaurant(name: "Po's Atelier", image: "posatelier"),
Restaurant(name: "Bourke Street Bakery", image: "bourkestreetbakery"),
Restaurant(name: "Haigh's Chocolate", image: "haighschocolate")]
Each item in the array represents a restaurant object containing both the restaurant name and the image. Once you update the code in XCode you will see an error saying that the restaurantNames variable is missing. This is expected because we just deleted it. Now update the body variable like this:
struct ContentView: View {
var body: some View {
var restaurants = [ Restaurant(name: "Cafe Deadend", image: "cafedeadend"),
Restaurant(name: "Homei", image: "homei"),
Restaurant(name: "Teakha", image: "teakha"),
Restaurant(name: "Cafe Loisl", image: "cafeloisl"),
Restaurant(name: "Petite Oyster", image: "petiteoyster"),
Restaurant(name: "For Kee Restaurant", image: "forkeerestaurant"), Restaurant(name: "Po's Atelier", image: "posatelier"), Restaurant(name: "Bourke Street Bakery", image: "bourkestreetbakery"), Restaurant(name: "Haigh's Chocolate", image: "haighschocolate")]
var restaurantImages = ["cafedeadend", "homei", "teakha", "cafeloisl", "petiteoyster", "forkeerestaurant", "posatelier", "bourkestreetbakery", "haighschocolate"]
List(restaurants, id: \.name) { restaurant in
HStack {
Image(restaurant.image)
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(restaurant.name)
}
}
.listStyle(.plain)
}
}
The result of the code would be the following:
Take a look at the parameters we passed to List. Instead of passing the range, we now pass the restaurants array and specify the name property as the identifier for each restaurant. List will iterate through the array and give us the current restaurant being processed.
Inside the closure, we define how we want to present each restaurant row. In this case, we use an HStack to display both the image and the name of the restaurant.
The resulting UI remains unchanged, but the underlying code was modified to leverage List with a collection of data.