iOS: How to create a custom presenter row in Eureka

Alfredo Luco G
5 min readMar 14, 2020
Photo by Arnel Hasanovic on Unsplash

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.

Photo by Helloquence on Unsplash

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!

Photo by bruce mars on Unsplash

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

First VC
Second VC
first VC

You can find that example in my github.

Happy coding!

--

--