0

Starting with this

var body: some View {
    ScrollView {
        VStack(spacing: 0.0) {
            Some views here
        }
    }
    .edgesIgnoringSafeArea(.top)
}

How would I add

            List(suggestions, rowContent: { text in
                NavigationLink(destination: ResultsPullerView(searchText: text)) {
                    Text(text)
                }
            })
            .searchable(text: $searchText)

on top if that scrollable content? Cause no matter how I hoax this together when

@State private var suggestions: [String] = []

gets populated (non empty) the search results are not squeezed in (or, better yet, shown on top of

"Some views here"

So what I want to achieve in different terms: search field is on top, scrollable content driven by the search results is underneath, drop down with search suggestions either temporarily squeeses scrollable content down or is overlaid on top like a modal sheet. Thanks!

Anton Tropashko
  • 5,486
  • 5
  • 41
  • 66

1 Answers1

1

If you are looking for UIKit like search behaviour you have to display your results in an overlay:

1. Let's declare a screen to display the results:

struct SearchResultsScreen: View {
    
    @Environment(\.isSearching) private var isSearching
    var results: [String]?
    
    var body: some View {
        if isSearching, let results {
            if results.isEmpty {
                Text("nothing to see here")
            } else {
                List(results, id: \.self) { fruit in
                    NavigationLink(destination: Text(fruit)) {
                        Text(fruit)
                    }
                }
            }
        }
    }
}

2. Let's have an ObservableObject to handle the logic:

class Search: ObservableObject {
    
    static private let fruit = [
        "Apples ",
        "Cherries ",
        "Pears ",
        "Oranges ",
        "Pineapples ",
        "Bananas "
    ]
    
    @Published var text: String = ""
    
    var results: [String]? {
        if text.isEmpty {
            return nil
        } else {
            return Self.fruit.filter({ $0.contains(text)})
        }
    }
}

3. And lastly lets declare the main screen where the search bar is displayed:

struct ContentView: View {
    
    @StateObject var search = Search()
    
    var body: some View {
        NavigationView {
            LinearGradient(colors: [.orange, .red], startPoint: .topLeading, endPoint: .bottomTrailing)
                .overlay(SearchResultsScreen(results: search.results))
                .searchable(text: $search.text)
                .navigationTitle("Find that fruit")
        }
    }
}
LuLuGaGa
  • 13,089
  • 6
  • 49
  • 57
  • It works with LinearGradient in NavigationView but does not in my case of var body: some View { NavigationView { ScrollView { VStack(spacing: 0.0) {. Where would I put overlay/searchable/navtitle for the searchbar to appear? – Anton Tropashko Nov 25 '22 at 14:27
  • It makes no difference if I wire up with overlay.searchable ScrollView or VStack (or navview even) -> search bar does not show up :-[ – Anton Tropashko Nov 25 '22 at 14:29
  • And it was hidden due to .navigationBarHidden(true) I guess I need to shownavbar when user taps on search only but that's another question – Anton Tropashko Nov 25 '22 at 14:33
  • Definitely you have to have overlay followed by searchable - other way round it doesn't work. – LuLuGaGa Nov 25 '22 at 14:57
  • I would attach both to the outside of the scroll view – LuLuGaGa Nov 25 '22 at 14:58
  • Thanks. Do you know whw in my case the nav title shows up but the search field is hidden until ya scroll? – Anton Tropashko Nov 25 '22 at 15:16
  • there is placement argument to searchable which defines this behaviour – LuLuGaGa Nov 25 '22 at 16:09
  • e.g `placement: .navigationBarDrawer(displayMode: .always)` should ensure it stays visible – LuLuGaGa Nov 25 '22 at 16:10
  • Yes, thank you. It does not hide on scroll than. It seems to be all or nothing. It's just a bug on iOS with the .default. It's fine on iPadOS. – Anton Tropashko Nov 29 '22 at 08:48