-
-
Save bithavoc/f5c9d06f024bfb5d9d3bd9452b94849f to your computer and use it in GitHub Desktop.
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 | |
} | |
I'm having some trouble getting this to work - can you give me some pointers on how to set options to a type of ComposableSearchableItem? Thanks!
Can you give me some evidence how to call ComposableSearchablePushRow in my existing FormViewController? In search I'm looking for a specific String value in an given string array.
String doesn't adhere to SearchableItem so you need to wrap your String in something that does, say X.
<<< ComposableSearchablePushRow<X>() {
$0.title = "X"
$0.options = sequenceOfX.map {
return ComposableSearchableItem<X>.existingItem($0)
}
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"
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.
@bithavoc Getting a lot of errors using Swift 3 and lastest Eureka release, any cues?
Thanks!
EDIT: Solved, I had a
BaseCell
class in my project as well :(