iOS: How to create a custom presenter row in Eureka
These days I’ve been implementing eureka’s framework but I stopped a moment in the push presenter row. There’s no tutorial to implement that kind of rows, there’s only the definitions for that rows so I only accomplish the implementation by forums. If you are reading this you’ll learn how to implement a custom push eureka row.
The purpose of implementing the presenter eureka row is you need to see a list, for example, and after selecting a row you must put that value in the current form.
First Step: Install eureka’s pod
First of all you need to install cocoapods in your proyect
pod init
Then, you must open Podfile’s file and put the Eureka’s pod:
pod 'Eureka'
Your podfile must say the following:
# Uncomment the next line to define a global platform for your projectplatform :ios, '13.0'target 'PresenterRowSample' do# Comment the next line if you don't want to use dynamic frameworksuse_frameworks!# Pods for PresenterRowSamplepod 'Eureka'end
Now You must run the following command:
pod install
Second Step: Set the custom presenter cell
We’re going to open PresenterRowSample.xcworkspace and we’ll see an empty project. Now, you must add a new group called Cells.
In that group you must put two files; CustomPresenterCell.swift and CustomPresenterCell.XIB
The first file we gonna add new file > Cocoa Touch Class > Subclass of UITableViewCell. The code inside that class may be the following:
import UIKitimport Eurekapublic class PresenterRowSampleCell: PushSelectorCell<String>{ //MARK: - IBOutlets @IBOutlet weak var imageIcon: UIImageView! @IBOutlet weak var stackedLabel: UILabel! override public func setup() { super.setup() } override public func update() { super.update() if let value = row.value{ self.imageIcon.image = UIImage(named: value) } }}
As we can see, the cell inherits from PushSelectorCell class. This is a class from eureka that performs the behaviour of presenting a view controller. That view controller can be a tableViewController, collectionViewController or whatever we want.
Now we must create the XIB file and set the outlets in the interface builder.
In effects of that sample we’re going to make a custom cell with a label and an image:
Third Step: Make the row
Now we need to make the row’s class. This class must implement a required init and implement the customDidSelect method. That method calls the view controller to be push and listen when this will be dismissed.
//PresenterRowSample.swiftimport Foundationimport Eurekapublic final class PresenterRowSample: Row<PresenterRowSampleCell>, RowType { public required init(tag: String?) { super.init(tag: tag) cellProvider = CellProvider<PresenterRowSampleCell>(nibName: "PresenterRowSampleCell", bundle: Bundle(for: PresenterRowSampleCell.self)) displayValueFor = { guard let result = $0 else { return "" } self.value = result self.updateCell() return result } }public override func customDidSelect() { super.customDidSelect() guard !isDisabled else { return } let vc = SelectableViewController() vc.row = self cell.formViewController()?.navigationController?.pushViewController(vc, animated: true) vc.onDismissCallback = { _ in vc.navigationController?.popViewController(animated: true) } }}
Fourth Step: Selectable View Controller Creation
Now the easiest step; create our selectable view controller. That controller implements one protocol; TypedRowControllerType. That protocol declares the presented row as a var and dismiss callback as a closure:
public var row: RowOf<String>!public var onDismissCallback: ((UIViewController) -> Void)?
Before All We must declare a custom cell that contains an image as an IBOutlet:
import UIKitclass ImagePresentedCell: UITableViewCell { @IBOutlet weak var iconImageView: UIImageView! override func awakeFromNib() { super.awakeFromNib() } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) }}
Now, we’re going to create that controller with a tableView:
import UIKitimport Eurekapublic class SelectableViewController: UIViewController, TypedRowControllerType { //MARK: - IBOutlets @IBOutlet weak var tableView: UITableView! //MARK: - Variables open var images: [String] = [] public var row: RowOf<String>! public var onDismissCallback: ((UIViewController) -> Void)? //MARK: - Init public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } open override func loadViewIfNeeded() { super.loadViewIfNeeded() } public convenience init(images: [String]){ let name = String(describing: SelectableViewController.self) let bundle = Bundle(for: SelectableViewController.self) self.init(nibName: name, bundle: bundle) self.images = images self.loadViewIfNeeded() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override public func viewDidLoad() { super.viewDidLoad() self.tableView.delegate = self self.tableView.dataSource = self self.tableView.register(UINib(nibName: "ImagePresentedCell", bundle: Bundle(for: ImagePresentedCell.self)), forCellReuseIdentifier: "presentedIdentifier") }}extension SelectableViewController: UITableViewDelegate, UITableViewDataSource{ public func numberOfSections(in tableView: UITableView) -> Int { return 1 } public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.images.count } public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {if let cell = tableView.dequeueReusableCell(withIdentifier: "presentedIdentifier", for: indexPath) as? ImagePresentedCell{ cell.iconImageView.image = UIImage(named: self.images[indexPath.row]) return cell } return UITableViewCell()}public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let image = self.images[indexPath.row] self.row.value = image self.row.updateCell() guard let callback = self.onDismissCallback else{ return } callback(self) }}
Now we’re going to come back to the row class as we declared before and modify the customDidSelect implementation:
public override func customDidSelect() { super.customDidSelect() guard !isDisabled else { return } let vc = SelectableViewController(images: ["imageA","imageB"]) vc.row = self cell.formViewController()?.navigationController?.pushViewController(vc, animated: true) vc.onDismissCallback = { _ in vc.navigationController?.popViewController(animated: true) } }
The imageA and imageB are my own images
Last Step: Implement the form!
Finally we just implement the form in our view controller class:
import UIKitimport Eurekaclass ViewController: FormViewController { override func viewDidLoad() { super.viewDidLoad() form +++ Section("Presenter row") <<< PresenterRowSample(tag: "row").cellSetup({ cell, row in cell.stackedLabel.text = "Sample" }).cellUpdate({ cell,row in print(row.value) }) }
}
Results
You can find that example in my github.
Happy coding!