Reputation: 469
I have a tableView and a childController in a parent viewController, the tableView in the ParentViewController can have from 1 - 4 cells, each cell contains a UITextField.
The ChildController also have a TableView, that list results(autocomplete) based on what is inputted in any of the TextField in the ParentViewController tableView cell.
I want the childController to always listen to any of the UITextField and show the result on the tablView. This is what I have currently
private var query = Variable<String>("")
var queryDriver: Driver<String> {
return query.asDriver()
}
TableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView
.dequeueReusableCell(
withIdentifier: "StopCell", for: indexPath) as? StopCell else {
fatalError("Cannot dequeue StopCell")
}
cell.delegate = self
cell.locationTextField.rx.text.map {$0 ?? ""}
.bind(to: query)
.disposed(by: disposeBag)
cell.locationTextField.rx.controlEvent(.editingDidEnd)
.asDriver(onErrorJustReturn: ())
.drive(onNext: { [unowned self] in
cell.locationTextField.resignFirstResponder()
})
.disposed(by: disposeBag)
return cell
}
Add child controller
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let noteVC = NoteVc()
addChildController(viewController: noteVC)
}
NoteVC
class NoteVc: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(tableView)
viewModel = SearchLocationViewModel(query: <#T##SharedSequence<DriverSharingStrategy, String>#>)
}
ViewModel
class SearchLocationViewModel {
let disposeBag = DisposeBag()
// MARK: - Properties
var querying: Driver<Bool> { return _querying.asDriver() }
var locations: Driver<[Location]> { return _locations.asDriver() }
// MARK: -
var hasLocations: Bool { return numberOfLocations > 0 }
var numberOfLocations: Int { return _locations.value.count }
// MARK: -
private let _querying = BehaviorRelay<Bool>(value: false)
private let _locations = BehaviorRelay<[Location]>(value: [])
// MARK: -
private let disposeBag = DisposeBag()
// MARK: - Initializtion
init(query: Driver<String>) {
Behave.shared.queryDriver
.throttle(0.5)
.distinctUntilChanged()
.drive(onNext: { [weak self] (addressString) in
self?.geocode(addressString: addressString)
})
.disposed(by: disposeBag)
}
Like it is implemented in the Uber app, users can add up to three destinations, the yellow rectangle box in the image below is my ChildViewController
Upvotes: 1
Views: 1726
Reputation: 33967
Here's the simplest I could think of. I made viewModel a global constant but you might want to get more elaborate with it.:
class ViewModel {
let inputs: [AnyObserver<String>]
let outputs: [Observable<String>]
init(count: Int) {
let subjects = (0..<count).map { _ in BehaviorSubject<String>(value: "") }
inputs = subjects.map { $0.asObserver() }
outputs = subjects.map { $0.asObservable() }
}
}
class ParentViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let bag = self.bag
Observable.just(viewModel.inputs)
.bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { _, element, cell in
let textField = cell.viewWithTag(99) as! UITextField
textField.rx.text.orEmpty
.bind(to: element)
.disposed(by: bag)
}
.disposed(by: bag)
}
let bag = DisposeBag()
}
class ChildViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let bag = self.bag
Observable.just(viewModel.outputs)
.bind(to: tableView.rx.items(cellIdentifier: "Cell", cellType: UITableViewCell.self)) { _, element, cell in
element
.bind(to: cell.textLabel!.rx.text)
.disposed(by: bag)
}
.disposed(by: bag)
}
let bag = DisposeBag()
}
Upvotes: 3