Image row is a component which emerges usually when building form using eureka in iOS. There is a library implement image row but it is not customizable. Therefore I write this post to explain how to implement and customize image row to suit your needs.
Contents
Set up view in xib file
In Xcode menu click File > New > File > Cocoa Touch Class. In the dialog type name of the class as CustomImageCell:

Click Next > Create. You’ll see there is 2 file CustomImageCell.swift and CustomImageCell.xib added to Xcode.
Oen File CustomImageCell.xib, insert an UIImageView and UIButton, or you can copy content from the demo source.

Set up class CustomImageCell
Open file CustomImageCell.swift and update content of class CustomImageCell as follow:
import Eureka
public class CustomImageCell: Cell<UIImage>, CellType {
@IBOutlet weak var thumbnailView: UIImageView!
override public func update() {
super.update()
thumbnailView.image = row.value
}
@IBAction func imageClicked(_ sender: Any) {
row.didSelect()
}
}
Next open file CustomImageCell.xib and connect IBAction of UIButton to method imageClicked like this:

Set up class CustomImageRow
In file CustomImageCell.swift, add the following code:
public final class CustomImageRow: Row<CustomImageCell>, RowType {
required public init(tag: String?) {
super.init(tag: tag)
// We set the cellProvider to load the .xib corresponding to our cell
cellProvider = CellProvider<CustomImageCell>(nibName: "CustomImageCell", bundle: Bundle.main)
}
public override func customDidSelect() {
super.customDidSelect()
let picker = UIImagePickerController()
picker.delegate = self
cell.formViewController()?.present(picker, animated: true, completion: nil)
}
}
extension CustomImageRow: UIImagePickerController, UINavigationControllerDelegate {
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
value = image
updateCell()
picker.dismiss(animated: true, completion: nil)
}
}
}
There are 2 things need to noted when using Eureka:
Cellonly does one job that is display UI and receive event UI.- Logic and remaining tasks will be handled by
Row
Handle callback of UIImagePickerController
If you run the project by now, you’ll see Xcode shows the following error:
Type 'CustomImageRow' does not conform to protocol 'NSObjectProtocol'.
Do you want to add protocol stubs?
To fix this problem, we’re gonna create an intermediate class which inherits from NSObject then forward callback back to CustomImageRow.
Create class PickerPropagator with the following content:
import Foundation
import UIKit
protocol PickerPropagatorDelegate: AnyObject {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any])
func imagePickerControllerDidCancel(_ picker: UIImagePickerController)
}
class PickerPropagator: NSObject, UIImagePickerControllerDelegate & UINavigationControllerDelegate {
weak var delegate: PickerPropagatorDelegate?
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
delegate?.imagePickerControllerDidCancel(picker)
}
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
delegate?.imagePickerController(picker, didFinishPickingMediaWithInfo: info)
}
}
In class CustomImageRow, add property pickerPropagator to forward callback from UIImagePickerController by changing CustomImageRow to the following:
public final class CustomImageRow: Row<CustomImageCell>, RowType {
private let pickerPropagator: PickerPropagator = PickerPropagator()
required public init(tag: String?) {
super.init(tag: tag)
// We set the cellProvider to load the .xib corresponding to our cell
cellProvider = CellProvider<CustomImageCell>(nibName: "CustomImageCell", bundle: Bundle.main)
pickerPropagator.delegate = self
}
public override func customDidSelect() {
super.customDidSelect()
let picker = UIImagePickerController()
picker.delegate = pickerPropagator
cell.formViewController()?.present(picker, animated: true, completion: nil)
}
}
extension CustomImageRow: PickerPropagatorDelegate {
public func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
value = image
updateCell()
picker.dismiss(animated: true, completion: nil)
}
}
}
By now the implementation of custom image row is completed. Next we’ll add it to the form.
Integrate CustomImageRow into view controller
In class ViewController, add the following code in viewDidLoad:
form +++ Section("Section1")
<<< CustomImageRow() { row in
}.cellSetup({ (cell, row) in
cell.height = { 100 }
})
Next, add the following code at the end of class ViewController to prevent tap outside of UIButton:
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if tableView.cellForRow(at: indexPath) is CustomImageCell {
return nil
}
return super.tableView(tableView, willSelectRowAt: indexPath)
}
Now you can run the project and try the demo.
Wraps Up
Here are keypoints when implementing custom image row:
Cellonly does one job that is display UI and receive event UI.- Logic and remaining tasks will be handled by
Row. - Use intermediate class to forward callback if
Rowcannot handle directly.
Hope this post could be useful to you. If there is anything please let me know in the comment section. Have a nice day!
