Imagine wanting to put a array of items in a simple list and present it. Without the need to create table view subclass and go into delegate and datasource.
let models = ["Apple", "Microsoft", "Google"]
Describe what a cell would do for a single model.
let cellDescs = models.map { m -> ListCellDescriptor<String, SimpleCell> in
var cd = ListCellDescriptor(m, identifier: identifier, cellClass: SimpleCell.self,
// 1. Configure
configure: { cell in
cell.textLabel?.text = m
})
// set action handler
cd.onSelect = { [weak self] in
self?.tapped(m)
}
return cd
}
SimpleCell used above is just a empty subclass of normal UITableViewCell. Prefer to subclass and provide custom design that matches your business logic.
class SimpleCell: UITableViewCell { }
SectionDescriptors are just a way to group CellDescriptors. Think of them as array of each section's cell view model.
/// A SectionDescriptor is made up of array of cell descriptor.
let sections = ListSectionDescriptor(with: cellDescs)
/// A List is made of up array of section.
let list = ListViewController(with: [sections])
If your model gets updated later on after the list is already on screen, simply pass the new sectionDescriptors
to the list's update
method.
list.update(with: newSections)
The ListViewController
uses FastDiff
, a very fast diffing algorithm with time complexity of O(nm), to perform updates with standard table view animations.
That's it.
Note
So far we have seen how to present one type of model with single type/variant of cell. We even had just 1 section. This is called homogeneous typed list.
Can we support multiple sections?
First off this is how list is modelled.
List == [SectionDescriptor<T>] // 1
SectionDescriptor<T> == [CellDescriptor<T,U>] where U: UITableViewCell // 2
With this approach
- A list can have same typed sections
- Each section can have same typed cells
- The type
T
is the model type. Since we can deduce U to subclass of UITableViewCell, its omitted in sections for brevity.
Thus we can try to erase type of model T
to AnyHashable
to be able to construct heterogeneous list. This is made incredibly easy and safe by the library by exposing any()
functions on both ListCellDescriptor
and SectionCellDescriptor
.
struct ModelItem: Hashable {
let color: UIColor
let int: Int
}
let modelsForNextSection = [ModelItem(color: .red, int: 1), .init(color: .blue, int: 2), .init(color: .purple, int: 3)]
let cellDescs2 = modelsForNextSection.map { m in
return ListCellDescriptor(m, identifier: identifier2, cellClass: SimpleCell.self, configure: { cell in
cell.textLabel?.text = "\(m.int)"
cell.backgroundColor = m.color // setting color for the cell
})
}
let secondSection = ListSectionDescriptor(with: cellDescs2)
Given ListSectionDescriptor<String>
and ListSectionDescriptor<ModelItem>
, we need to pack them into single array. To do so, we erase model type.
/// note the .any()
let combinedSections = [sections.any(), secondSection.any()]
let list = ListViewController(with: combinedSections)
NOTE:
With this approach one can combine any kinds of cellDescriptors and sectionDescriptors without introducing the type erasure or casting while actually declaring both cellDescriptor and sectionDescriptor for particular models.
See picture for heterogeneous list
Imagine we want to present Int and String typed models in same section using SimpleCell and AnotherCell.
class AnotherCell: UITableViewCell { }
let mc1 = ListCellDescriptor(1, identifier: "mc1", cellClass: SimpleCell.self, configure: { cell in
cell.textLabel?.text = "\(1)"
})
/// look out for cell identifier
let mc2 = ListCellDescriptor("hello", identifier: "mc2", cellClass: AnotherCell.self, configure: { cell in
cell.textLabel?.text = "hello"
cell.backgroundColor = .purple
})
Now since we want to contain varying types of models in 1 section, our section has to be type erased.
/// .any() on ListCellDescriptor
let mixedSection = ListSectionDescriptor(with: [mc1.any(), mc2.any()])
Just append it to the previous 2 examples we build upon and display.
let combinedSections = [sections.any(), secondSection.any(), mixedSection]
let list = ListViewController(with: combinedSections)
NOTE:
This now gives us full power of having complete heterogeneous list if we choose to.
TBD
This is a tiny library that eliminates rudimentary and repetitive UITableView subclassing, delegates and datasource handling for every simple list we want to present. It also provides efficient updates out of the box. Why not just declare what to make a list of and watch it.
This is by no means a extensive list library, for that always use either UITableView or UICollectionView. This is useful in cases where you want to throw in a list either for demo, prototype or simple read only list.