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.