Editable navigation titles in SwiftUI on iOS 16
Starting from iOS and iPadOS 16 we have a new version of the navigationTitle(_:) modifier that accepts a binding to a string. We can use this modifier to let users rename items straight from the navigation title in the toolbar. This functionality could be useful in apps where an item is configurable, similar to a document in document-based apps.
In this post we will be looking at a simplified example of a note taking application, that displays a note list in the sidebar and a note editor in the detail view.
The initial code for the note detail view, where we see a text editor and the note name in the navigation bar, could be the following.
struct NoteView: View {
@ObservedObject var note: Note
var body: some View {
TextEditor(text: $note.text)
.padding()
.navigationTitle(note.title)
}
}
We would like to let users rename notes from inside the detail view by typing a new name in the navigation bar title. In iOS 16 we can do that by passing a binding to a string instead of a plain string value to the navigationTitle()
modifier. It's important to also set the navigation title display mode to inline
, otherwise the editing functionality won't work. To be able to extract the binding from the title string, the title
property of the Note
object will need to be marked with @Published
.
struct NoteView: View {
@ObservedObject var note: Note
var body: some View {
TextEditor(text: $note.text)
.padding()
.navigationTitle($note.title)
.navigationBarTitleDisplayMode(.inline)
.toolbarRole(.editor)
}
}
class Note: Identifiable, ObservableObject {
@Published var title: String
@Published var text: String
var id: UUID
...
}
By setting the toolbar role to editor
in iOS 16 we indicate that we would like users to edit the item in the detail view rather than just browse it. This places the navigation title on the leading edge of the bar leaving space for toolbar buttons in the middle. You can learn more about toolbars in iOS/iPadOS 16 from my previous article Customizable toolbar on iPad in SwiftUI.
When we pass a binding to the navigationTitle()
SwiftUI automatically changes the appearance of the title and adds an arrow indicator to show that there are actions available. Pressing on the arrow reveals the rename action that is also added automatically.
This functionality works on both iPhone and iPad, but the title is always centered in compact size class, independent of the toolbar role setting.
To make sure that the name of the note updates in the sidebar when the user finishes editing it from the navigation title in the detail view, we should make the list row in the sidebar observe the Note
object.
struct NoteListRow: View {
@ObservedObject var note: Note
var body: some View {
NavigationLink(value: note.id) {
HStack {
Text(note.title)
Spacer()
}
}
}
}
We already had to mark the title
of the Note
with @Published
to extract a binding for it to pass to the navigationTitle()
modifier. Now that the row view is observing the Note
, the view will get reevaluated when the title
changes.
You can get the full sample for this post from our GitHub repo.