lagoupo
lagoupo

Reputation: 99

.searchable() - make the search box stick to the top without moving when focused

I am trying to make the search box in .searchable stick to the top without moving when there is no navigationBarTitle. Basically, when the box gets focused, there is an animation that moves the box upward and I would like to prevent that from hapenning. I just want the searchbox to stay stationary at the top, with just the cancel button coming from the right when the box is focused.

I tried all type of things such as .navigationBarHidden(true) but this totally removes the search bar as a whole.

Tried to add some spacer under in a vstack but it didnt push the bar up when unfocused, i tried to add some .ignoreSafeArea() but it didnt work. I tried to add a geometryReader and make the screen height larger than the display but it didnt move things up.

My codes:

struct SearchBarSM: View {
    
    @State var searchtxt:String = ""
    
    var body: some View {
        GeometryReader { geo in
            NavigationStack {
                ZStack {
                    VStack {
                        Text("Hello stackoverflow")
                    }
                }
                .navigationBarTitle("")
                .searchable(text: $searchtxt, prompt: "Search") {
                }
                .preferredColorScheme(.dark)
            }
        }
    }
}

When Unfocused:

enter image description here

When Focused:

enter image description here

The box moves up when focused.

Upvotes: 4

Views: 2811

Answers (2)

Sweeper
Sweeper

Reputation: 273510

As devdchaudhary said in the comments, I doubt this can be changed. You need to make your own search bar, and put it in the .principal position of the toolbar.

It's not that difficult to make your own search bar. Here, I have made something that behaves similar to the system's search bar. Adjust the colors and paddings as you see fit.

struct CustomSearchBar: View {
    @Binding var searchText: String
    
    @State var active = false
    
    var body: some View {
        HStack {
            HStack {
                Image(systemName: "magnifyingglass").foregroundColor(.gray)
                TextField("Search", text: $searchText, onEditingChanged: { editing in
                    withAnimation {
                        active = editing
                    }
                })
            }
            .padding(7)
            .background(Color(white: 0.9))
            .cornerRadius(10)
            .padding(.horizontal, active ? 0 : 50)
            
            Button("Cancel") {
                withAnimation {
                    active = false
                }
            }
            .opacity(active ? 1 : 0)
            .frame(width: active ? nil : 0)
        }
    }
}

Usage:

.toolbar {
    ToolbarItem(placement: .principal) {
        CustomSearchBar(searchText: $searchText)
    }
}

Upvotes: 2

baohoang
baohoang

Reputation: 788

To achieve the behavior you described, you have to make a search bar view custom and then put it into the SearchBarSM view.

Example code:

SearchableCustom: View

struct SearchableCustom: View {
    
    @Binding var searchtxt: String
    @FocusState private var isSearchFocused: Bool // Track focus state
    
    var body: some View {
        HStack {
            HStack {
                Image(systemName: "magnifyingglass")
                TextField("Search", text: $searchtxt)
                    .focused($isSearchFocused) // Track focus state
                    .padding(.horizontal, 10)
                    .padding(.vertical, 8)
            }
            .padding(.horizontal)
            .background(Color(.secondarySystemBackground))
            .cornerRadius(10)
            .overlay(RoundedRectangle(cornerRadius: 10).stroke(Color.gray, lineWidth: 1))
            
            if isSearchFocused {
                Button("Cancel") {
                    searchtxt = ""
                    withAnimation(.spring()) {
                        isSearchFocused = false
                    }
                }
                .transition(.move(edge: .trailing)) // Add animation for cancel button
            }
        }
        .frame(maxWidth: .infinity)
        .padding(.horizontal)
    }
}

SearchBarSM: View

struct SearchBarSM: View {
    
    @State var searchtxt:String = ""
    
    var body: some View {
        VStack {
            SearchableCustom(searchtxt: $searchtxt)
            Text("Hello stackoverflow")
        }
        .frame(maxHeight: .infinity, alignment: .top)
        .preferredColorScheme(.dark)
    }
}

Upvotes: 1

Related Questions