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

Check out our book!

Integrating SwiftUI into UIKit Apps

Integrating SwiftUI intoUIKit Apps

UPDATED FOR iOS 17!

A detailed guide on gradually adopting SwiftUI in UIKit projects.

  • Discover various ways to add SwiftUI views to existing UIKit projects
  • Use Xcode previews when designing and building UI
  • Update your UIKit apps with new features such as Swift Charts and Lock Screen widgets
  • Migrate larger parts of your apps to SwiftUI while reusing views and controllers built in UIKit

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