WWDC23 deal: 40% off our book "Integrating SwiftUI into UIKit apps"! Learn more ...WWDC23 deal:40% off "Integrating SwiftUI into UIKit apps" >>

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()
    }
}
Integrating SwiftUI into UIKit Apps by Natalia Panferova book coverIntegrating SwiftUI into UIKit Apps by Natalia Panferova book cover
WWDC23 offer
Our book "Integrating SwiftUI into UIKit apps" is now 40% off!

Discover various ways to adopt SwiftUI in existing UIKit projects and take full advantage of the new iOS 16 frameworks and APIs such as Swift Charts.

The offer is active until the 19th of June.

# 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.