-
-
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
BaseCellclass in my project as well :(