In-Line Editing of a List Item

I have a list of text items. I would like to be able to edit an item inside the list. I would tap an item and it would become a TextField, where I can edit the text. Then, when I leave the field or tap Done, it goes back to a Text item with the new data.

Any idea how to accomplish this?

Use TextField for the list items.

On iOS you may want to disable the text field until the person taps the Edit button. If the list items are navigation links, you probably want to disable the text field because people select list items more often than they rename them.

To disable text fields until the person taps the Edit button, you need to add two properties to the view and two modifiers to the text field.

The first property is an environment variable that tracks when someone taps the Edit and Done buttons. The second property tracks whether the text field is disabled.

@Environment(\.editMode) private var editMode
@State private var disableTextField = true

The first text field modifier is .disabled. The second modifier is onChange , where you track the change in edit mode.

TextField("", text: $item.name)
  .disabled(disableTextField)
  .onChange(of: editMode?.wrappedValue) { oldValue, newValue in
    if (newValue != nil) && (newValue!.isEditing) {
      // Edit button tapped
      disableTextField = false
    }
    else {
      // Done button tapped
      disableTextField = true
    }

More details are in the following article:

Disable a Text Field in a SwiftUI List Until Tapping Edit Button

1 Like

Thanks for your suggestion. The list items are not NavigationLinks. I want to just be able to type into any list item and have that item change to the new value.

I tried this:

@State private var newText = ""
.
.
.
List(items)
{ item in
   TextField(item,name, text: $newText)
}

But when I type the new text, all the items are changed to the new text.

How can I change only the text in the item I’m typing in?

I should mention using $item.name, as suggested, but it caused and error:

ā€œCould not find ā€˜$item’ in scopeā€.

The reason all the items are changed when you enter new text is because each list item’s text field is bound to newText. You have to bind the text field to one of the list item’s properties. Get rid of newText and pass the list item’s property as the text argument of the text field.

List(items) { $item in
   TextField("", $item.name)
}

Replace $item.name with the list item property you are showing in the list. Notice that you need a $ character in front of item after the opening brace to create the binding that the text field needs.

When someone taps on a list item, they will be able to rename the item. The downside of this behavior is people will be renaming the item every time they tap on a list item. That is why I mentioned disabling the text field until someone taps the Edit button in my original reply.

Well, I learned something again. I never thought about putting the $ there. Thanks for the tip. I’ll have to try it tomorrow.

The reason I don’t think I need to use the Edit button is that the only reason the item would be tapped is to change the name. Currently, tapping the item brings up a new view where the name can be changed. I thought I could streamline it a bit.

Thanks for the help.

I got this error for List(items) { $item:
ā€œInferred projection type ā€˜Item’ is not a property wrapperā€

I recommend asking a new question about the syntax error you’re getting. Show all of the SwiftUI code for the view that contains the list in the new question. There isn’t enough information in the error message and the code snippets you showed for anyone to help you fix the error. For example, you haven’t shown any code for Item. Without that code no one can tell you whether or not Item is a property wrapper.

Your original question was a general one: how to edit an item inside a list. I answered that question: use TextField for the list items. Now you are getting specific compiler errors trying to use a text field in a list. That calls for a new question.