nickreps
nickreps

Reputation: 1080

SwiftUI - Dismiss Searchbar

I am using the new SwiftUI searchbar .searchable. In conjunction with this, I am using a method that allows an action to be performed when search is clicked: .onSubmit(of: .search) {

The issue I am having is I cannot figure out how to dismiss the searchbar when .onSubmit is triggered.

the .onSubmit is working fine and I am able to call functions from inside of it, but cannot figure out how to dismiss the searchbar itself. The keyboard disappears, but the searchbar itself remains in an active state.

I have tried resorting to UIKit methods, but they do not seem compatible.

Any ideas?

Upvotes: 1

Views: 1113

Answers (2)

Michael Ellis
Michael Ellis

Reputation: 378

The accepted answer is not quite correct. I just came across this problem and did not want to get my same question flagged as duplicate so I asked on the Apple Forums and had help finding a solution.

In order to call dismissSearch from a parent view's .onSubmit(of: .search) one must find a way to store the reference to the child view's dismissSearch environment value in a way that the parent can call it. For example, use a ViewModel of some kind to store a function that you can overwrite from the child with the child's dismissSearch environment value when it becomes available in an onAppear or something similar.

class ViewModel {
    var dismissClosure: () -> Void = { print("Not Set") }
}

Pass this ViewModel to the child and then store the dismissSearch in the child so the parent can call it from the shared ViewModel

.onAppear {
    viewModel.dismissClosure = { dismissSearch() }
}

Here is the complete solution to the posted problem that can pasted into an Xcode Playground to test it out.

import SwiftUI
import PlaygroundSupport

class ViewModel {
    var dismissClosure: () -> Void = { print("Not Set") }
}
struct SearchingExample: View {
    @State var searchText = ""
    @State var didSubmit = false
    @Environment(\.dismissSearch) var dismissSearch
    let viewModel = ViewModel()
    
    var body: some View {

        NavigationStack {
            SearchedView(didSubmit: $didSubmit, viewModel: viewModel)
                .searchable(text: $searchText)
                .onSubmit(of: .search) {
                    didSubmit = true
                    viewModel.dismissClosure()
                }
        }
    }
}

struct SearchedView: View {
    @Environment(\.isSearching) var isSearching
    @Environment(\.dismissSearch) var dismissSearch
    @Binding var didSubmit: Bool
    let viewModel: ViewModel

    var body: some View {
        VStack {
            Text(isSearching ? "Searching!" : "Not searching.")
            Button(action: { dismissSearch() }, label: {
                Text("Dismiss Search")
            })
            Button(action: {
                // Return Key for playground
            }, label: {
                Image(systemName: "paperplane")
            })
            .frame(width: 30, height: 30)
            .keyboardShortcut(.defaultAction)
            if didSubmit {
                Text("You Submitted Search!")
                    .onAppear {
                        Task { @MainActor in
                            try await Task.sleep(nanoseconds: 3_000_000_000)
                            self.didSubmit = false
                        }
                    }
            }
        }
        .onAppear {
            viewModel.dismissClosure = { dismissSearch() }
        }
    }
}

PlaygroundPage.current.needsIndefiniteExecution = true
PlaygroundPage.current.setLiveView(SearchingExample())

Upvotes: 1

Florian S
Florian S

Reputation: 614

There is a new environment value

@Environment(\.dismissSearch) var dismissSearch

that you can call as a function to dismiss the searchbar.

Upvotes: 2

Related Questions