Last active
April 23, 2024 13:32
-
-
Save bithavoc/f5c9d06f024bfb5d9d3bd9452b94849f to your computer and use it in GitHub Desktop.
Eureka Swift 3 Composable Search Row
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open class _ComposableSearchablePushRow<T: Equatable, Cell: CellType> : TableSelectorRow<Cell, ComposableSearchableViewController<T>> where Cell: BaseCell, Cell: TypedCellType, Cell.Value == ComposableSearchableItem<T>, T: SearchableItem, T: CustomStringConvertible { | |
public required init(tag: String?) { | |
super.init(tag: tag) | |
onCreateControllerCallback = { [weak self] _ in | |
let controller = ComposableSearchableViewController<T>() | |
controller.searchPlaceholder = self?.searchPlaceholder | |
return controller | |
} | |
} | |
var searchPlaceholder: String? | |
} | |
/// Selector Controller (used to select one option among a list) | |
open class ComposableSearchableViewController<T:Equatable> : _ComposableSearchableViewController<T, ListCheckRow<ComposableSearchableItem<T>>, T> where T:SearchableItem, T: CustomStringConvertible { | |
} | |
open class _ComposableSearchableViewController<T: Equatable, Row: SelectableRowType, TOriginal:Equatable> : UITableViewController, UISearchResultsUpdating, TypedRowControllerType where Row: BaseRow, Row: TypedRowType, Row.Cell.Value == ComposableSearchableItem<T>, T: SearchableItem, T: CustomStringConvertible, TOriginal: SearchableItem, TOriginal: CustomStringConvertible { | |
/// A closure to be called when the controller disappears. | |
public var onDismissCallback: ((UIViewController) -> ())? | |
open var row: RowOf<Row.Cell.Value>! | |
let searchController = UISearchController(searchResultsController: nil) | |
required public init() { | |
super.init(style: .grouped) | |
self.navigationItem.titleView = self.searchController.searchBar | |
searchController.searchResultsUpdater = self | |
searchController.dimsBackgroundDuringPresentation = false | |
searchController.hidesNavigationBarDuringPresentation = false | |
self.definesPresentationContext = true | |
} | |
required public init?(coder aDecoder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
var originalOptions = [ComposableSearchableItem<T>]() | |
var currentOptions = [ComposableSearchableItem<T>]() | |
var searchPlaceholder: String? | |
open override func viewDidLoad() { | |
super.viewDidLoad() | |
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") | |
searchController.searchBar.placeholder = searchPlaceholder | |
//tableView!.tableHeaderView = searchController.searchBar | |
if let options = row.dataProvider?.arrayData { | |
self.originalOptions = options | |
self.currentOptions = options | |
} | |
self.tableView.reloadData() | |
if let composableItem = row.value { | |
switch composableItem { | |
case .composedQuery(let query): | |
searchController.searchBar.text = query | |
case .existingItem: | |
if let index = currentOptions.index(of: composableItem) { | |
let indexPath = IndexPath(index: index) | |
self.tableView.selectRow(at: indexPath, animated: false, scrollPosition: .top) | |
} | |
} | |
} | |
} | |
fileprivate func filter(_ query: String) { | |
if query == "" { | |
currentOptions = self.originalOptions | |
} else { | |
currentOptions = self.originalOptions.filter{ $0.matchesSearchQuery(query) } | |
if currentOptions.isEmpty { | |
currentOptions.append(ComposableSearchableItem<T>.composedQuery(query)) | |
} | |
} | |
self.tableView.reloadData() | |
} | |
open override func numberOfSections(in tableView: UITableView) -> Int { | |
return 1 | |
} | |
open override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { | |
return self.row?.title | |
} | |
open override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { | |
return currentOptions.count | |
} | |
open override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { | |
let option = self.currentOptions[indexPath.row] | |
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) | |
switch(option) { | |
case .composedQuery(let query): | |
let text = NSMutableAttributedString() | |
text.append(NSAttributedString(string: "Otro: ", attributes: [NSFontAttributeName: UIFont.boldSystemFont(ofSize: UIFont.labelFontSize)])) | |
text.append(NSAttributedString(string: query)) | |
cell.textLabel?.attributedText = text | |
case .existingItem(let item): | |
cell.textLabel?.text = item.description | |
} | |
return cell | |
} | |
open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { | |
let option = self.currentOptions[indexPath.row] | |
row.value = option | |
onDismissCallback?(self) | |
} | |
open func updateSearchResults(for searchController: UISearchController) { | |
filter(searchController.searchBar.text!) | |
} | |
} | |
public final class ComposableSearchablePushRow<T: Equatable> : _ComposableSearchablePushRow<T, PushSelectorCell<ComposableSearchableItem<T>>>, RowType where T: SearchableItem, T: CustomStringConvertible { | |
public required init(tag: String?) { | |
super.init(tag: tag) | |
} | |
} | |
public enum ComposableSearchableItem<T:Equatable> : Equatable, CustomStringConvertible, SearchableItem where T: SearchableItem, T: CustomStringConvertible { | |
case existingItem(T) | |
case composedQuery(String) | |
public var description: String { | |
switch(self) { | |
case .existingItem(let item): | |
return item.description | |
case .composedQuery(let query): | |
return query | |
} | |
} | |
public func matchesSearchQuery(_ query: String) -> Bool { | |
switch(self) { | |
case .existingItem(let item): | |
return item.matchesSearchQuery(query) | |
case .composedQuery(let q): | |
return q == query | |
} | |
} | |
} | |
public func ==<T>(lhs: ComposableSearchableItem<T>, rhs: ComposableSearchableItem<T>) -> Bool { | |
switch (lhs, rhs) { | |
case let (.existingItem(l), .existingItem(r)): | |
return l == r | |
case let(.composedQuery(l), .composedQuery(r)): | |
return l == r | |
default: | |
return false | |
} | |
} | |
/// Generic row type where a user must select a value among several options. | |
open class TableSelectorRow<Cell: CellType, VCType: TypedRowControllerType>: OptionsRow<Cell> where Cell: BaseCell, VCType: UITableViewController, VCType.RowValue == Cell.Value { | |
open var onCreateControllerCallback : ((FormViewController)->(VCType))? | |
required public init(tag: String?) { | |
super.init(tag: tag) | |
} | |
/** | |
Extends `didSelect` method | |
*/ | |
open override func customDidSelect() { | |
super.customDidSelect() | |
if isDisabled { | |
return | |
} | |
guard let createController = onCreateControllerCallback else { | |
return | |
} | |
let controller = createController(cell.formViewController()!) | |
prepareSelector(controller: controller) | |
let formViewController = cell.formViewController()! | |
formViewController.show(controller, sender: nil) | |
} | |
/** | |
Prepares the pushed row setting its title and completion callback. | |
*/ | |
open override func prepare(for segue: UIStoryboardSegue) { | |
super.prepare(for: segue) | |
guard let controller = segue.destination as? VCType else { return } | |
prepareSelector(controller: controller) | |
} | |
fileprivate func prepareSelector(controller: VCType) { | |
controller.row = self | |
controller.title = selectorTitle ?? controller.title | |
controller.onDismissCallback = {vc in | |
_ = vc.navigationController?.popViewController(animated: true) | |
} | |
} | |
} | |
public protocol SearchableItem { | |
func matchesSearchQuery(_ query: String) -> Bool | |
} | |
Guys, if anyone interested, I just modified this gist to follow the simpler SearchablePushRow
found in the original post from the author:
http://bithavoc.io/blog/2016/07/04/eureka-search-push-row/
Code:
https://gist.github.com/maxhanglin/032033405a2717a4cdd445bf5190b65f
It can be used as:
<<< SearchablePushRow<Contact>("person") { row in
row.options = contactsList
}
Contact, in this example, needs to be:
public class Contact: CustomStringConvertible, Equatable, SearchableItem { ... }
I am kind of new to using Eureka forms...Do you have any of you have any working examples that you could offer, for download, to see this in action?
I made another version of SearchPushRow which works with Eureka 3 and Swift 3.1:
https://gist.github.com/noefroidevaux/d033e4fb4382f0dcfc877fa4e6ba95a2
In this version, I simply subclasses _SelectorViewController and add the UISearchController.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Seeing this warning which I haven't dived into yet: "Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior"