Skip to content

Using Table Schemer

James Richard edited this page Jul 30, 2014 · 14 revisions

The primary class in TableSchemer is the TableScheme class. This class implements the UITableViewDataSource protocol. It contains an array of SchemeSet objects, which map to a table view section. Each SchemeSet has an array of Scheme objects. Each Scheme object represents one or more cells in your table.

Creating a TableScheme

The recommended approach to creating a TableScheme is using the closure syntax provided by the init(buildHandler) convenience initializer. This allows you to group the table scheme creation in one block, and visually see how the layout of the table will look. Here's an example of creating a single BasicScheme cell:

let tableScheme = TableScheme() { (builder) in
    builder.buildSchemeSet { (builder) in
        builder.name = "Section Header"
                
        builder.buildScheme { (scheme: BasicScheme) in
            scheme.reuseIdentifier = "CellIdentifier"
                    
            scheme.configurationHandler = { (cell) in
                cell.textLabel.text = "My Cell"
            }
        }
    }
}

Note that the ReuseIdentifier used must be registered with the UITableView instance that the tableScheme is the data source for, or the cell won't be able to be created.

Each Scheme object has an isValid property. This property determines if the Scheme has all the required properties configured on it. This property is checked by the builder object before instantiating the SchemeSet. If it doesn't have the correct properties, an assertion will be thrown. This allows you to be sure your scheme is configured correctly at development time. If the TableScheme is created in Release builds, and it doesn't pass validation, it won't be added to the SchemeSet.

Schemes use lots of optionals

Schemes make heavy use of optionals to allow easy configuration. However, every scheme has some required information. By using the builder closures we'll call isValid on the scheme object before adding it to the SchemeSet to ensure all the required properties for that scheme are defined.

Schemes are Generic

All built-in Scheme objects require at least one generic type to represent the UITableViewCell that it vends. Some Schemes can have multiple cell types, but due to how typing works in Swift, you'll have to do a conditional cast to get the proper cell type when configuring.

If your Scheme is just a regular UITableViewCell, you don't need to provide the generic, as shown above. If it does, you'll just pass the type like so:

builder.buildScheme { (scheme: BasicScheme<InputFieldCell>) in 
}

When creating a custom Scheme, you aren't forced to use Generics like the built-in Schemes, but it is recommended. More details on that in Creating Custom Schemes.

Handling Selection

TableScheme allows you to set selection handlers on your Scheme classes. Because TableScheme conforms to UITableViewDataSource, and height is part of UITableViewDelegate, you'll have to forward selection from your delegate to the TableScheme class. This was designed this way so you can keep your view controller the delegate, as is typical, and use the TableScheme for the datasource.

Selections are handled by the TableScheme like so:

func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
    tableScheme.handleSelectionInTableView(tableView, forIndexPath: indexPath)
}

Handling Height

If you aren't handling height through Auto Layout you can have height handled by your scheme objects. Each Scheme object handles height in its own way. See the Built-in Schemes section for more details on that.

Because TableScheme conforms to UITableViewDataSource, and selection is part of UITableViewDelegate, you'll have to forward height calculations from your delegate to the TableScheme class. This was designed this way so you can keep your view controller the delegate, as is typical, and use the TableScheme for the datasource.

tableView(tableView: UITableView!, heightForRowAtIndexPath indexPath: NSIndexPath!) -> CGFloat {
    return tableScheme.heightInTableView(tableView, forIndexPath: indexPath)
}

Interactions Inside Cells

If you're interacting with views inside the cell through Target/Action or some other mechanism other than cell selection you may need to know which scheme is triggering it. Here's a basic of a method used to handle switches from multiple schemes:

func switcherUpdated(switcher: UISwitch) {
    if let scheme = tableScheme.schemeContainingView(switcher) {
        if scheme === self.firstSwitchScheme {
            self.wifiEnabled = switcher.on
        } else if scheme === self.secondSwitchScheme {
            self.bluetoothEnabled = switcher.on
        }
    }
}

Sometimes though, you'll be working with an ArrayScheme or RadioScheme. In those instances you may need to know which item within those schemes was selected. Here's how you'd find that:

func buttonPressed(button: UIButton) {
    if let tuple = tableScheme.schemeWithIndexContainingView(button) {
        if tuple.scheme === self.buttonsScheme {
            let object = self.buttonsScheme.objects![tuple.index]
        }
    }
}

You can see the full source code for those examples in the example project in AdvancedTableSchemeViewController.swift.

Check out the Built-in Schemes page for details about each Scheme provided with TableSchemer.

Clone this wiki locally