Filter List (Numeric)
NumericFilterListConnector( numericFilters: [NumericFilter], selectionMode: SelectionMode, filterState: FilterState, operator: RefinementOperator, groupName: String, controller: FilterListTableController<Filter.Numeric> )
About this widget
NumericFilterList
is a filtering component that displays numeric filters and lets the user refine the search results by selecting them.
Compared to the RefinementList
, which takes its values from the search response facets, this widget displays numeric filters that you add yourself.
Examples
Instantiate a NumericFilterListConnector
and launch an initial search on its Searcher
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let searcher = HitsSearcher(appID: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
indexName: "YourIndexName")
let filterState = FilterState()
let filters: [Filter.Numeric] = [
.init(attribute: "price", operator: .lessThan, value: 5),
.init(attribute: "price", range: 5...10),
.init(attribute: "price", range: 10...25),
.init(attribute: "price", range: 25...100),
.init(attribute: "price", operator: .greaterThan, value: 100)
]
let filterListTableView: UITableView = .init()
let filterListController: FilterListTableController<Filter.Numeric> = .init(tableView: filterListTableView)
let filterListConnector = NumericFilterListConnector(numericFilters: filters,
selectionMode: .multiple,
filterState: filterState,
operator: .and,
groupName: "Numeric Filters",
controller: filterListController)
searcher.connectFilterState(filterState)
searcher.search()
Parameters
numericFilters
|
type: [NumericFilter]
default: []
Required
The numeric filters to display. |
selectionMode
|
type: SelectionMode
default: .single
Optional
Whether a user can select |
filterState
|
type: FilterState
Required
The |
operator
|
type: RefinementOperator
Required
Whether we apply an For example if we have an |
groupName
|
type: String
Required
Filter group name. |
controller
|
type: FilterListTableController<Filter.Numeric>
default: nil
Optional
Controller interfacing with a concrete filter list view. |
Low-level API
If you want to fully control the NumericFilterList
components and connect them manually, use the following components:
Searcher
: TheSearcher
that handles your searches.FilterState
: The current state of the filters.NumericFilterListInteractor
: The logic applied to the numeric filters.FilterListController
: The view that will render the numeric filters.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let searcher = HitsSearcher(appID: "YourApplicationID",
apiKey: "YourSearchOnlyAPIKey",
indexName: "YourIndexName")
let filterState = FilterState()
let filters: [NumericFilter] = [
.init(attribute: "price", operator: .lessThan, value: 5),
.init(attribute: "price", range: 5...10),
.init(attribute: "price", range: 10...25),
.init(attribute: "price", range: 25...100),
.init(attribute: "price", operator: .greaterThan, value: 100)
]
let filterListInteractor = NumericFilterListInteractor(items: filters,
selectionMode: .multiple)
filterListInteractor.connectFilterState(filterState,
operator: .and,
groupName: "Numeric Filters")
searcher.connectFilterState(filterState)
searcher.search()
Customizing your view
The default controllers, e.g., FilterListTableController
, work well when you want to use native UIKit with their default behavior.
If you want to use another component such as a UICollectionView
, a third-party input view, or you want to introduce some custom behavior to the already provided UIKit component, you can create your own controller conforming to the NumericFilterListController
protocol.
Protocol
var onClick: ((Filter.Numeric) -> Void)?
:
Closure to call when a filter is clicked.
func setSelectableItems(selectableItems: [SelectableItem<Filter.Numeric>])
Function called when a new array of selectable facets is updated. This is the UI State of the refinement list.
func reload()
Function called when we require a reload of the list view.
Implementation example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class NumericFilterListTableController: NSObject, NumericFilterListController, UITableViewDataSource, UITableViewDelegate {
public var onClick: ((Filter.Numeric) -> Void)?
public let tableView: UITableView
public var selectableItems: [SelectableItem<Filter.Numeric>] = []
public var filterPresenter: FilterPresenter?
let cellID: String
public init(tableView: UITableView, cellID: String = "cellID") {
self.tableView = tableView
self.cellID = cellID
super.init()
tableView.dataSource = self
tableView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
}
// MARK: - FilterListController
public func setSelectableItems(selectableItems: [SelectableItem<Filter.Numeric>]) {
self.selectableItems = selectableItems
}
public func reload() {
tableView.reloadData()
}
// MARK: - UITableViewDataSource
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return selectableItems.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
let filter = selectableItems[indexPath.row]
let filterPresenter = self.filterPresenter ?? DefaultPresenter.Filter.present
cell.textLabel?.text = filterPresenter(Filter(filter.item))
cell.accessoryType = filter.isSelected ? .checkmark : .none
return cell
}
// MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectableItem = selectableItems[indexPath.row]
onClick?(selectableItem.item)
}
}
SwiftUI
InstantSearch provides the FilterList
SwiftUI view which you can embed in your views.
It uses FilterListObservableController
as a data model, which is an implementation of the SelectableListController
protocol adapted for usage with SwiftUI.
FilterListObservableController
must be connected to the NumericFilterListConnector
or NumericFilterListInteractor
like any other SelectableListController
implementation.
You have to define the appearance of the view representing a single numeric filter and its selection state.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct ContentView: View {
@ObservedObject var filterListController: FilterListObservableController<Filter.Numeric>
var body: some View {
FilterList(filterListController) { filter, isSelected in
// declare the view presenting a single numeric filter and its selection state
HStack {
Text(filter.description)
Spacer()
if isSelected {
Image(systemName: "checkmark")
.foregroundColor(.accentColor)
}
}
.contentShape(Rectangle())
.frame(idealHeight: 44)
.padding(.horizontal, 5)
}
}
}
If you prefer to create a custom SwiftUI view that presents the list of numeric filters, you can directly use the FilterListObservableController<Filter.Numeric>
as a data model.
It provides filters
and selections
properties along with toggle
and isSelected
functions to streamline the design process of your custom SwiftUI view.
Check out the example to see this widget in action.