Find and replace in iOS and iPadOS 16

Apple introduced a redesigned find and replace experience in iOS and iPadOS 16. In SwiftUI this functionality is automatically supported by TextEditor view.

Screenshot of iPad simulator with a text editor and find and replace interface activated

We also have some new APIs to customize find and replace in our apps.

# Disable replace

We can disable support for replace functionality by applying replaceDisabled() modifier to TextEditor.

struct TextView: View {
    @State private var text = ""
    
    var body: some View {
            TextEditor(text: $text)
                .replaceDisabled()
    }
}

# Disable find and replace

If we want to entirely disable the feature, we can use findDisabled() modifier. It will disable both find and replace and user won't be able to summon the system find UI when interacting with our TextEditor.

struct TextView: View {
    @State private var text = ""
    
    var body: some View {
        TextEditor(text: $text)
            .findDisabled()
    }
}
Swift Gems by Natalia Panferova book coverSwift Gems by Natalia Panferova book cover

Level up your Swift skills!$35

100+ tips to take your Swift code to the next level

Swift Gemsby Natalia Panferova

  • Advanced Swift techniques for experienced developers bypassing basic tutorials
  • Curated, actionable tips ready for immediate integration into any Swift project
  • Strategies to improve code quality, structure, and performance across all platforms

Level up your Swift skills!

100+ tips to take your Swift code to the next level

Swift Gems by Natalia Panferova book coverSwift Gems by Natalia Panferova book cover

Swift Gems

by Natalia Panferova

$35

# Programmatically present system find UI

To programmatically summon the find interface, we can use findNavigator(isPresented:) modifier and pass it a binding to a Boolean value.

struct TextView: View {
    @State private var text = ""
    @State private var showFindUI = false
    
    var body: some View {
        TextEditor(text: $text)
            .findNavigator(isPresented: $showFindUI)
            .toolbar {
                Toggle(isOn: $showFindUI) {
                    Label(
                        "Find",
                        systemImage: "magnifyingglass"
                    )
                }
            }
    }
}

Note that if we want to disable replace inside the find UI that is presented programmatically, we have to make sure that replaceDisabled() is placed closer to TextEditor than findNavigator(isPresented:).

TextEditor(text: $text)
    .replaceDisabled()
    .findNavigator(isPresented: $showFindUI)

If we use findDisabled() in conjunction with findNavigator(isPresented:), setting isPresented binding of the find navigator to true won't do anything, because findDisabled() completely disables the feature.

Find and replace is only supported inside TextEditor at the moment, TextField doesn't have this functionality.

You can find a sample project for this post on GitHub.