▶hlongvu

Opensourcing My UITableview Model View Library, Helps to Create Complex UI Screens by UITablview Easier and Cleaner - Inspired by Airbnb Epoxy Library

Inspired by Airbnb/Epoxy library, I recreate a small and lightweight similar library for iOS.

SGView

Introduction

To build a long and complex UI screen is a tedious task. We often using UITableView to make this kind of screen, but we find ourselves making DataSource and Delegate again and again. This library handles all the complex and repeatedly task when dealing with UITableView DataSource and Delegate, and showing a nicer and cleaner API.

People also searching for how to work with multiples view type inside UITableView, this helps to solve them as well.

Structure

The core of this library is two simple class: SGModel and SGAdapter.

SGAdapter is a class holds a list of SGModels and abstracts all UITableViewDataSource and UITableViewDelegate methods. This class will deal with all change and edit inside UITableView.

Take a look at SGAdapter setUp function you can see a glimpse of what its job:

 open func setUp(_ tv:UITableView){
        self.tableView = tv
        registerNib(tableView: tableView!)
        self.modelList = buildModels()
        self.tableView?.dataSource = self
        self.tableView?.delegate = self
    }

So by adding models into SGAdapter, we can add rows into UITableView.

SGModel is another class that represents a row inside UITableView, it helps SGAdapter initiate new row and fillData into that row. Two most important function inside SGModel are: nibName() and fillData()

  • nibName(): return the identifier for UITableView to set as reuseIdentifier
  • fillData(): this function run inside cellForRowAt-indexPath of UITableViewDelegate. It will fill the data for the row at indexPath. Like we usually inititate data when creating new row.

Usage

Simply create a new MainAdapter that inherit SGAdapter and override two most important functions:

class MainAdapter: SGAdapter {
    var loadView: Int = 0
    override func registerNib(tableView: UITableView) {
        tableView.registerCellNib(ImageCell.typeName)
        tableView.registerCellNib(ButtonCell.typeName)
        tableView.registerCellClass(LabelCell.self)
        tableView.registerCellClass(ColorCell.self)
    }
    
    override func buildModels() -> [SGModel] {
        var list = [SGModel]()       
        list.append(ColorCellModel("color1", .black))
        list.append(ColorCellModel("color2", .blue))
        list.append(ColorCellModel("color3", .yellow))
        list.append(ColorCellModel("color4", .brown))
        list.append(ColorCellModel("color5", .green))
    }
}

This will create an adapter that hold an array of Colors models. The registerNib() function will register cell for reuse in UITableview.

How to create Models?

As usual, we can create a normal UITableViewCell like below:

class ColorCell: UITableViewCell {
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String!)
    {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        self.contentView.heightAnchor.constraint(equalToConstant: 40).isActive = true
    }
}

A model is a wrapper around an UITableViewCell and holds information needed to fill data to that cell:

class ColorCellModel: SGModel{
    var color: UIColor!
    
    init(_ id: String, _ color: UIColor) {
        super.init(id)
        self.color = color
    }
    
    override func nibName() -> String {
        return ColorCell.typeName
    }
    
    override func fillData(cell: UITableViewCell) {
        super.fillData(cell: cell)
        cell.selectionStyle = .none
        if let c = cell as? ColorCell{
            c.backgroundColor = self.color
        }
    }
}

We can see that ColorCell is only bare minium creation of an UITableViewCell contains only view and boutlets, and most of the data and action are put into ColorCellModel.

Finally, inside UIViewcontrolelr, we can setup our adapter:

adapter = MainAdapter()
adapter?.loadView = 0
adapter?.setUp(self.tableView)

Whenever we change modelList inside Adapter, we can call adapter.reBuildModelsAndReloadTable to reload the tableView.

You can see more usage and model examples inside Example Project at https://github.com/hlongvu/SGView. SGModel is flexible and can be used with Nib based UITableViewCell, or programmatically UITableViewCell subclass.

Furthur Detail

This SGView library include a Diff calculating library, helps to animate the change inside UITablView, which code is from https://github.com/mcudich/HeckelDiff

Conclusion

Compare with Epoxy View Library for Android, this is really simple and not has as many features. But SGView helps a lot in simplifying working with UITableview. I have been using it inside my apps and feel my code is more elegant and nicer to work with. I can forget UITableViewDataSource and UITableViewDelegate and all the repeatedly task writing my code for them.

Hope you can give it a try and leave some comments.

Check out the code at: https://github.com/hlongvu/SGView