Skip to content

bealex/AwesomeTableAnimationCalculator

Repository files navigation

Awesome Table Animation Calculator

There are times when you need to determine what was changed in a table (collection) model to update it with animations. It can be even more complex task when sections are involved.

Awesome Table Animation Calculator provides simple interface for this task. It holds data model for the table and can calculate animatable difference for some changes (and apply them to the UICollectionView/UITableView afterwards).

CocoaPods Compatible Carthage compatible GitHub license BuddyBuild

###Usage

Installation

  • manually, just get source files from Code/ATableAnimation
  • CocoaPods: pod 'AwesomeTableAnimationCalculator'
  • Carthage: github "bealex/AwesomeTableAnimationCalculator"

Implement Cell and Section models. These models define equality for cells (both id-equality and contents equality) and sections. Here is a simple example.

public class ASectionModelExample: ASectionModel, Equatable {
    public let title:String

    public init(title:String) {
        self.title = title
        super.init()
    }
}

public func ==(lhs:ASectionModelExample, rhs:ASectionModelExample) -> Bool {
    return lhs.title == rhs.title
}

Cells are just a little bit harder.

class ACellModelExample: ACellModel {
    var id:String
    var header:String
    var text:String

    init(text:String, header:String) {
        id = NSUUID().UUIDString
        self.text = text
        self.header = header
    }

    required init(copy:ACellModelExample) {
        id = copy.id
        text = copy.text
        header = copy.header
    }

    func contentIsSameAsIn(another:ACellModelExample) -> Bool {
        return text == another.text
    }
}

func ==(lhs:ACellModelExample, rhs:ACellModelExample) -> Bool {
    return lhs.id == rhs.id
}

Class that connects sections and cells together:

class ACellSectionModelExample: ACellSectionModel {
    required init() {
    }

    func cellsHaveSameSection(one one:ACellModelExample, another:ACellModelExample) -> Bool {
        return one.header == another.header
    }

    func createSection(forCell cell:ACellModelExample) -> ASectionModelExample {
        return ASectionModelExample(title:cell.header)
    }
}

Create AnimationCalculator and set the cellModelComparator there for the correct cells sorting.

private let calculator = ATableAnimationCalculator(cellSectionModel: ACellSectionModelExample())

// somewhere in init or viewDidLoad
calculator.cellModelComparator = { left, right in
    return left.header < right.header
           ? true
           : left.header > right.header
               ? false
               : left.text < right.text
}

After that you can use methods of AnimationCalculator for your dataSource methods implementation.

func numberOfSectionsInTableView(tableView:UITableView) -> Int {
    return calculator.sectionsCount()
}

func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int {
    return calculator.itemsCount(inSection:section)
}

func tableView(tableView:UITableView, 
        cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell {
    var cell = tableView.dequeueReusableCellWithIdentifier("generalCell")
    cell!.textLabel!.text = calculator.item(forIndexPath:indexPath).text
    return cell!
}

func tableView(tableView:UITableView, titleForHeaderInSection section:Int) -> ing? {
    return calculator.section(withIndex:section).title
}

Now magic starts. You can simply change whole model like this (no animation yet):

try! calculator.setItems([
        ACellModelExample(text:"1", header:"A"),
        ACellModelExample(text:"2", header:"B"),
        ACellModelExample(text:"3", header:"B"),
        ACellModelExample(text:"4", header:"C"),
        ACellModelExample(text:"5", header:"C")
])

tableView.reloadData()

You can change just a subset of cells (with animation):

let addedItems = [
    ACellModelExample(text:"2.5", header:"B"),
    ACellModelExample(text:"4.5", header:"C"),
]

let itemsToAnimate = try! calculator.updateItems(addOrUpdate:addedItems, delete:
itemsToAnimate.applyTo(tableView:tableView)

If you've changed comparator, you can simply resort model:

let itemsToAnimate = try! calculator.resortItems()
itemsToAnimate.applyTo(tableView:self.tableView)