Leave a rating/review
Take a look at these two initializers. One is for a List view, and the other one, a ForEach view.
But if I hadn’t told you that, you might have thought I accidentally duplicated the content on my slide.
As it turns out, a List is not only for displaying a collection of similar views. That functionality is the specific responsibility of ForEach.
As you see here, in this other list initializer, you can put any View you want into a List, just like with a “body” property.
List’s job is primarily to create a column of rows. But none of them actually have to be repeated!
Which means that we can put other single-use views into our list: like this “Add New Book” button.
To do that, we’ll need to start by breaking a ForEach away from our List initializer:
List(library.sortedBooks) { book in
Try just changing List to ForEach.
ForEach(library.sortedBooks)
It still renders, but you just get one huge row! Without a List wrapping a ForEach, that’s what you’ll get. So, embed the ForEach in a List.
NavigationView {
List(/*@START_MENU_TOKEN@*/0 ..< 5/*@END_MENU_TOKEN@*/) { item in
ForEach(library.sortedBooks) { book in
This looks right, but if you start the live preview… You’ll see that you’ve got five ForEaches stacked vertically on top of eachother! So, not quite right.
We don’t need to iterate. All we need is to put our ForEach in the List’s content closure.
NavigationView {
List {
ForEach(library.sortedBooks) { book in
Move the nav bar title back onto the List view, with option-command-right-bracket…
}
.navigationBarTitle("My Library")
}
}
}
…and we’re back to the original result! Except now, we can add other views that are not BookRows into the list. Like a button with a label that says “Add New Book”.
List {
Button {
} label: {
Text("Add New Book")
}
ForEach(library.sortedBooks) { book in
Give that the title2 font…
Text("Add New Book")
.font(.title2)
}
…and embed it in a VStack.
VStack {
Text("Add New Book")
.font(.title2)
}
ForEach(library.sortedBooks) { book in
Put a “book dot circle” symbol in there with it.
VStack {
Image(systemName: "book.circle")
Text("Add New Book")
That is too small. Use a system font at 60 points, so we can see it.
Image(systemName: "book.circle")
.font(.system(size: 60))
Text("Add New Book")
Then put just a little more space between them.
VStack(spacing: 6) {
Add some spacers, to center the VStack.
} label: {
Spacer()
VStack(spacing: 6) {
Image(systemName: "book.circle")
.font(.system(size: 60))
Text("Add New Book")
.font(.title2)
}
Spacer()
}
That’s not quite right, but instead of embedding this whole thing in an HStack… Apply a borderless button style to the whole button, and give it some vertical padding, to match the book rows.
}
.buttonStyle(.borderless)
.padding(.vertical, 8)
You can add as many different views as you want to your lists. It’s just so common to only use a bunch of the same row view, that Apple combined the List and ForEach initializers for that use case. So, keep things simple, and make use of those, until you need more complexity.