'SwiftUI: dismiss List searchable keyboard?

I have a view that uses searchable. I would like do dismiss the keyboard programmatically if user starts scrolling on the List. Same behavior as you see in the Contacts app.

@State var query:String = ""

var body: some View {
    NavigationView {
        List {
            ForEach(items) { item in
                NavigationLink {
                    Text("Item at \(item.timestamp!, formatter: itemFormatter)")
                } label: {
                    Text(item.timestamp!, formatter: itemFormatter)
                }
            }
            .onDelete(perform: deleteItems)
        }
        .searchable(text: $query, prompt: Text("search"))
        .simultaneousGesture(DragGesture().onChanged({ _ in
            /// <-- dismiss keyboard??
        }))
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                EditButton()
            }
            ToolbarItem {
                Button(action: addItem) {
                    Label("Add Item", systemImage: "plus")
                }
            }
        }
        Text("Select an item")
    }
}

I can detect the scroll event, but, I am not sure how to actually dismiss the keyboard.

I saw some approaches using endEditing on the view... but, I wonder about using focuseScope. I found some information about it here:

https://swiftwithmajid.com/2020/12/02/focus-management-in-swiftui/

How can I dismiss the keyboard, and preferably, use focus instead of calling something generic on the whole view?



Solution 1:[1]

Use the dismissSearch environment value to get the DismissSearchAction instance for the current Environment. Then call the instance to dismiss the current search interaction.

When you dismiss the search, SwiftUI sets isSearching to false and removes focus from the search field.

@State var query:String = ""
@Environment(\.dismissSearch) private var dismissSearch

var body: some View {
    NavigationView {
        List {
            ForEach(items) { item in
                NavigationLink {
                    Text("Item at \(item.timestamp!, formatter: itemFormatter)")
                } label: {
                    Text(item.timestamp!, formatter: itemFormatter)
                }
            }
            .onDelete(perform: deleteItems)
        }
        .searchable(text: $query, prompt: Text("search"))
        .simultaneousGesture(DragGesture().onChanged({ _ in
            dismissSearch()
        }))
        .toolbar {
            ToolbarItem(placement: .navigationBarTrailing) {
                EditButton()
            }
            ToolbarItem {
                Button(action: addItem) {
                    Label("Add Item", systemImage: "plus")
                }
            }
        }
        Text("Select an item")
    }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Fawzi Rifa'i