Creating editable Collections
Editing lists has always been possible in SwiftUI but before WWDC 2021 and SwiftUI 3, doing so was very inefficient because SwiftUI did not support binding to Collections. Let's use bindings on a collection and discuss how and why it works better now.
Getting ready
Create a new SwiftUI project and name it EditableListsFields
.
How to do it…
Let's create a simple to-do list app with a few editable items. The steps are as follows:
- Add a
TodoItem
struct below theimport
SwiftUI line:struct TodoItem: Identifiable { let id = UUID() var title: String init(_ someTitle:String){ title = someTitle } }
- In our
ContentView
struct, let's add a collection ofTodoItem
instances:@State var todos = [ TodoItem("Eat"), TodoItem("Sleep"), TodoItem("Code") ]
- Replace the
Text
view in the body with aList
and aTextField
view that displays the collection oftodo
items:var body: some View { List($todos) { $todo in TextField("Number", text: $todo.title) } }
Run the preview in canvas. You should be able to edit the text in each row, as shown in the following screenshot:
Click on any of the other rows and edit it to your heart's content.
How it works…
Let's start by looking at how editable lists were handled before SwiftUI 3. Before SwiftUI 3, the code for an editable list of items would use list indices to create bindings to a collection, as follows:
List(0..<todos.count) { index in TextField("Todo", text: $todos[index].title) }
Not only was such code slow, but editing a single item caused SwiftUI to re-render the entire List
of elements, leading to flickering and slow UI updates.
With SwiftUI 3, we can pass a binding to a collection of elements, and SwiftUI will internally handle binding to the current element specified in the closure. Since the whole of our collection conforms to the Identifiable
protocol, each of our list items can be uniquely identified by its id
parameter; therefore, adding or removing items from the list does not change list item indices and does not cause the entire list to be re-rendered.