1

Making SwiftUI Views Searchable

 3 years ago
source link: https://useyourloaf.com/blog/making-swiftui-views-searchable/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

SwiftUI gains a search field in iOS 15. The searchable view modifier lets you make any content view searchable reducing the need to roll your own search bar.

Searching SwiftUI Views

If you’ve ever struggled with UIKit’s UISearchController you’re probably going to like how much less effort it takes to add search to a SwiftUI view. Here’s a list of countries that I want to search:

List of countries

The implementation of the view is plain list in a navigation view:

struct WorldView: View {
  @FetchRequest(sortDescriptors: [SortDescriptor(\.name)])
  private var countries: FetchedResults<Country>
       
  var body: some View {
    NavigationView {
      List(countries) { country in
        NavigationLink(destination: 
          CountryView(country: country)) {
          CountryCell(country: country)
        }
      }
      .listStyle(.plain)
      .navigationTitle("World")

      NoCountrySelected()
    }
  }
}

I’m using Core Data to store the country objects but that’s not a requirement (see Configuring SwiftUI Fetch requests for a recap).

The Searchable View Modifier

To get started with adding search to our list we need a state variable to store our search query text:

@State private var query = ""

Then we add the .searchable view modifier to our content view. Note that you are not limited to searching lists, you can add the modifier to any content view:

NavigationView {
}
.searchable(text: $query)

Finally, we use a change in the query text to update our fetch request to generate new results:

NavigationView { ...
}
.searchable(text: $query)
.onChange(of: query) { newValue in
  countries.nsPredicate = searchPredicate(query: newValue)
}

Since we’re using Core Data we update the predicate of the fetch request using the query text:

private func searchPredicate(query: String) -> NSPredicate? {
  if query.isBlank { return nil }
    return NSPredicate(format: "%K BEGINSWITH[cd] %@",
    #keyPath(Country.name), query)
  }
}

Search bar in navigation view

Placeholder Text

By default, the search field shows a localized search prompt:

Search placeholder text

You can override this by adding a prompt to the searchable view modifier:

.searchable(text: $query, prompt: "Search countries")

Search countries placeholder text

Search Bar Placement

The search bar appears in a position appropriate to the platform. When used with a navigation view on iOS and iPadOS it defaults to applying to the primary view of a two column split view or the second column of a triple column view. You can always override that by adding the modifier directly on the desired column.

You can also change the preferred placement of the search field within the containing view hierarchy. The default is to place the search field automatically depending on the platform. You can override the default to prefer the navigationBarDrawer, sidebar or toolbar:

.searchable(text: $query, placement: .sidebar)

Note this has no effect in my example as I don’t have a sidebar. When shown below the navigation bar, the search bar collapses under the bar when the user scrolls. You can override that to keep the search bar visible by changing the display mode:

.searchable(text: $query,
  placement: .navigationBarDrawer(displayMode: .always))

Search bar visible when scrolling

isSearching and dismissSearch

The isSearching environment variable tells you if the user is interacting with a search field:

@Environment(\.isSearching)
private var isSearching: Bool

In the WWDC session video (see below) they use this to present the search results in an overlay. I’ve also found it useful when I want to show a search scope bar when the user is searching:

if isSearching {
  ScopeBar(selected: $searchBy)
}

The dismissSearch environment property gives you a method that dismisses an active search:

@Environment(\.dismissSearch)
private var dismissSearch

Calling dismissSearch() will also set isSearching to false:

if isSearching {
  Button("Dismiss") {
    dismissSearch()
  }
}

Search Submit Action

If you only want to perform the search when the user submits the query add an onSubmit action:

.onSubmit(of: .search) {
  // perform query
}

Search Suggestions

Finally, you can provide a search suggestions view as part of the searchable view modifier:

.searchable(text: $scope.beginsWith) {
  ForEach(suggestions) { suggestion in
    Text(suggestion.title).searchCompletion(suggestion.completion)
  }
}

I’m using Text views but you can use any view. The search completion provides the text that replaces the search query when the user selects a suggestion. I’m using a static list of suggestions, but you could generate them dynamically based, for example, on search history:

struct SearchSuggestion: Identifiable {
    var id: Int
    var title: String
    var completion: String
}

private var suggestions = [
  SearchSuggestion(id: 1, title: "🇨🇦 Canada", completion: "Canada"),
  SearchSuggestion(id: 2, title: "🇬🇧 UK", completion: "United Kingdom"),
  SearchSuggestion(id: 3, title: "🇺🇸 USA", completion: "United States")
]

Search suggestions

Learn More


Recommend

  • 52

    Part 2 in a series on understanding data in SwiftUI. We will talk about the key that makes principles inpart 1 possible in SwiftUI. And how this resulting in a reduction of the complexity of UI development.

  • 49
    • www.ioscreator.com 4 years ago
    • Cache

    SwiftUI Overlay Views Tutorial

    With SwiftUI layers can be added on top of the view. This can be done with the .overlay modifier. In this tutorial a text view is displayed on top of an image using an overlay.SwiftUI requires Xcode 11 and MacOS Catalina...

  • 33
    • swiftwithmajid.com 4 years ago
    • Cache

    Combine and SwiftUI views

    Combine is one of the new frameworks released during WWDC 2019. It provides a declarative Swift API for processing values over time. Today we will talk about one of the hidden features of SwiftUI...

  • 18

    SwiftUI provides us a very fast and easy to use diffing algorithm, but as you might know, diffing is a linear operation. It means that diffing will be very fast for simple layouts and can take some time for a c...

  • 25
    • swiftwithmajid.com 4 years ago
    • Cache

    Using UIKit views in SwiftUI

    A few weeks ago, we talked about building views like PagerView and BottomSheetView from scratch in SwiftUI . SwiftUI is pretty young and misses some components that we expect to hav...

  • 28
    • swiftwithmajid.com 4 years ago
    • Cache

    Fitting and filling views in SwiftUI

    This week I want to continue the topic of layout system in SwiftUI . The SwiftUI layout engine works predictably, and usually, an outcoming result looks like we expect. Today, to make this process even...

  • 9
    • worthdoingbadly.com 3 years ago
    • Cache

    Rendering SwiftUI views to HTML

    I built a proof-of-concept tool to render SwiftUI to HTML. While I’m not intending to turn it into a full UI framework, I still learned plenty along the way: I learned how to use Swift’s generic...

  • 12
    • looseyi.github.io 3 years ago
    • Cache

    WWDC20 - Build SwiftUI views for widgets

    WWDC20 Session 10033 - Build SwiftUI views for widgets 本文知识目录

  • 10

    Searchable modifier in SwiftUI: A UISearchController and UISearchBar equivalent 07 Jul 2021 ⋅ 16 min read ⋅ SwiftUI

  • 8

    Making Your Jekyll Blog Searchable with HTML, JavaScript and Google Nov 22, 2019 So yesterday someone said this to me: You know the problem with your blog – you wri...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK