alionthego
alionthego

Reputation: 9773

Is there a way to hide the search bar in SwiftUI

I have a view with the searchable modifier. It is always displaying a searchBar and works fine.

I find the searchBar is taking up too much space at the top of the view on an iPhone and since it is not used often, I want to only have a magnifying glass icon in the navigation bar. When the user presses that icon I want to show the search bar and hide it again after it becomes inactive.

Is there anyway to do this with the native searchBar (.searchable) without implementing a custom searchBar?

The view is not a list and I am not looking for the functionality of hiding the searchBar as the user scrolls. I just want to be able to control it's visibility with a button in the navigationBar.

struct SomeView: View {
    
    @Environment(\.dismissSearch) private var dismissSearch
    
    @State var searchString = ""
    
    var body: some View {
        NavigationStack {
            MyView()
                .searchable(text: $searchString, placement: .automatic)
                .onSubmit(of: .search) {
                    // Hide SearchBar
                }
                .toolbar {
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button {
                            // Show SearchBar
                        } label: {
                            Image(systemName: "magnifyingglass")
                        }
                    }
                }
        }
        
    }
}

Upvotes: 4

Views: 3252

Answers (1)

superpuccio
superpuccio

Reputation: 12992

At the moment there's no way to show/hide the search bar programmatically in SwiftUI. I think a solution can be found, but it strictly depends on your content view (I mean what you called MyView). If your MyView has a state that can be moved to a view model, you can easily do something like this:

struct SomeView: View {
    @State private var searchString = ""
    @State private var isSearchBarVisible = false
    @StateObject private var contentViewModel = MyViewModel()

    var body: some View {
        NavigationStack {
            if isSearchBarVisible {
                content
                    .searchable(text: $searchString, placement: .automatic)
            } else {
                content
            }
        }
    }

    @ViewBuilder
    private var content: some View {
        MyView(viewModel: contentViewModel)
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        isSearchBarVisible.toggle()
                    } label: {
                        Image(systemName: "magnifyingglass")
                    }
                }
            }
    }
}

class MyViewModel: ObservableObject {
    @Published private(set) var randomInt = 0

    func getRandomInt() {
        randomInt = Int.random(in: 0...999)
    }
}

struct MyView: View {
    @ObservedObject var viewModel: MyViewModel

    var body: some View {
        VStack {
            Text("\(viewModel.randomInt)")
            Button {
                viewModel.getRandomInt()
            } label: {
                Text("Tap to get a random int")
            }
        }
    }
}

The result is what you'd expect:

enter image description here

But, as I stated above, this solution works depending on your MyView. In order to help you find a solution, it would be great to have more details about your MyView.

EDIT: another, more complicated, example could be:

struct SomeView: View {
    @State private var searchString = ""
    @State private var isSearchBarVisible = false

    var body: some View {
        NavigationStack {
            MyView(searchText: $searchString, isSearchBarVisible: $isSearchBarVisible)
        }
    }
}

struct MyView: View {
    @Binding var searchText: String
    @Binding var isSearchBarVisible: Bool
    @State private var randomInt = 0

    var body: some View {
        ScrollView {
            VStack {
                Text("\(randomInt)")

                Button {
                    randomInt = Int.random(in: 0...999)
                } label: {
                    Text("Tap to get a random int")
                }

                if isSearchBarVisible {
                    titleView
                        .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always))
                } else {
                    titleView
                }

                ForEach(0...100, id: \.self) { n in
                    Text("\(n)")
                }
            }
        }
    }

    @ViewBuilder private var titleView: some View {
        Text("A list of numbers to take some vertical space:")
            .padding()
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    Button {
                        isSearchBarVisible.toggle()
                    } label: {
                        Image(systemName: "magnifyingglass")
                    }
                }
            }
    }
}

The result is:

enter image description here

Depending on your needs, I think you can find a solution.

Upvotes: 4

Related Questions