- Single data source can be used to design simple to complex every type of vertical listing using tableview.
- Protocol oriented datasource with Generic models and associated types.
- Native pull to refresh functionality handled in datasource with UIRefreshControl.
- Infinite scrolling of tableview is also handled for paging.
- Every UITableView class should conform to ReusableCell protocol by inheriting it.
- Every UITableViewHeaderFooterView class should confirm to ReusableHeaderFooterView protocol by inheriting it.
- Your model class for setting data in UITableViewCell should confirm to CellModelProvider protocol by inherinting it.
protocol CellModelProvider { typealias Property = (identifier: String, height: CGFloat, model: CellModelType?) associatedtype CellModelType var property: Property? { get set } var leadingSwipeConfig: SKSwipeActionConfig? { get set } var trailingSwipeConfig: SKSwipeActionConfig? { get set } init(_ _property: Property?, _ _leadingSwipe: SKSwipeActionConfig?, _ _trailingSwipe: SKSwipeActionConfig?) }
- Your model class for setting data in UITableViewHeaderFooterView should confirm to HeaderFooterModelProvider protocol by inheriting it.
protocol HeaderFooterModelProvider { typealias HeaderProperty = (identifier: String?, height: CGFloat?, model: HeaderModelType?) typealias FooterProperty = (identifier: String?, height: CGFloat?, model: FooterModelType?) associatedtype CellModelType : CellModelProvider associatedtype HeaderModelType associatedtype FooterModelType var headerProperty: HeaderProperty? { get set } var footerProperty: FooterProperty? { get set } var items: [CellModelType]? { get set } init(_ _header: HeaderProperty?, _ _footer: FooterProperty?, _ _items: [CellModelType]?) }
- For single listing with single cell types.
Consider you have following model.
You can declare and instantiate tableViewDataSource instance like below.
class Example01Model { var name: String? var description: String? var imageName: String? init(_ _name: String?, _ _desc: String?, _ _imageName: String?) { name = _name description = _desc imageName = _imageName } }
Setting data inside cellprivate var items = [Example01Model]() private var dataSource: TableDataSource<DefaultHeaderFooterModel<Example01Model>, //Model Type for header footer conforming HeaderFooterModelProvider protocol DefaultCellModel<Example01Model>, //Model Type for cell confirming CellModelProvider protocol Example01Model>? //Model Type of data to be used in cell dataSource = TableDataSource<DefaultHeaderFooterModel<Example01Model>, DefaultCellModel<Example01Model>, Example01Model>.init(.SingleListing(items: items, identifier: Example01Cell.identfier, height: height), tableView)
class Example01Cell: UITableViewCell, ReusableCell { //Inheriting Reuasable Cell Protocol typealias T = DefaultCellModel<Example01Model> //Declaring type of model to be used @IBOutlet weak var imgView: UIImageView! @IBOutlet weak var lblTitle: UILabel! @IBOutlet weak var lblDesc: UILabel! var item: DefaultCellModel<Example01Model>? { // variable from which we can set data in cell didSet { imgView.layer.cornerRadius = 16.0 imgView.layer.borderColor = UIColor.darkGray.cgColor imgView.layer.borderWidth = 0.8 imgView.image = UIImage(named: item?.property?.model?.imageName ?? "") lblTitle.text = item?.property?.model?.name ?? "" lblDesc.text = item?.property?.model?.description ?? "" } } }
- For multiple section and cell listing.
We need two models one for section footer and one for cell following rules that we've discussessed above.
You can declare and instantiate tableViewDataSource instance like below.
class SeasonHeaderFooterModel: HeaderFooterModelProvider { //conforming HeaderFooterModelProvider protocol typealias CellModelType = ActorCellModel //cell model type typealias HeaderModelType = Season //header model type typealias FooterModelType = Any //Footer model type var headerProperty: (identifier: String?, height: CGFloat?, model: Season?)? var footerProperty: (identifier: String?, height: CGFloat?, model: Any?)? var items: [ActorCellModel]? required init(_ _header: (identifier: String?, height: CGFloat?, model: Season?)?, _ _footer: (identifier: String?, height: CGFloat?, model: Any?)?, _ _items: [ActorCellModel]?) { headerProperty = _header footerProperty = _footer items = _items } } class ActorCellModel: CellModelProvider { //confirming CellModelProvider protocol typealias CellModelType = Actor // model type of data to be used in cell var property: (identifier: String, height: CGFloat, model: Actor?)? required init(_ _property: (identifier: String, height: CGFloat, model: Actor?)?) { property = _property } }
Setting data inside header footer viewprivate var items = [SeasonHeaderFooterModel]() private var dataSource: TableDataSource<SeasonHeaderFooterModel, ActorCellModel, Actor>? dataSource = TableDataSource<SeasonHeaderFooterModel, ActorCellModel, Actor>.init(.MultipleSection(items: items), tableView)
class TVSeriesHeaderView: UITableViewHeaderFooterView, ReusableHeaderFooter { typealias T = SeasonHeaderFooterModel // model type of data to be used in header footer view @IBOutlet weak var lblTitle: UILabel! var item: SeasonHeaderFooterModel? { didSet { lblTitle.text = item?.headerProperty?.model?.title ?? "" } } }
- Table View Data source blocks can be used for further functionality.
dataSource?.configureHeaderFooter = { (section, item, view) in (view as? TVSeriesHeaderView)?.item = item } dataSource?.configureCell = { (cell, item, indexPath) in (cell as? Example01Cell)?.item = item } dataSource?.addPullToRefresh = { [weak self] in self?.pageNo = 0 self?.getNewDataWhenPulled() } dataSource?.addInfiniteScrolling = { [weak self] in self?.pageNo = (self?.pageNo ?? 0) + 1 self?.addMoreDataWithPaging() } dataSource?.scrollDirection = { (direction) in switch direction { case .Up: break case .Down: break } } dataSource?.didSelectRow = { (indexPath, item) in }
- Leading and trailing swipe actions.
block that can be used to handle leading and trailing actions.
protocol CellModelProvider { typealias Property = (identifier: String, height: CGFloat, model: CellModelType?) associatedtype CellModelType var property: Property? { get set } var leadingSwipeConfig: SKSwipeActionConfig? { get set } // used to assign leading actions var trailingSwipeConfig: SKSwipeActionConfig? { get set } // used to assign trailing actions init(_ _property: Property?, _ _leadingSwipe: SKSwipeActionConfig?, _ _trailingSwipe: SKSwipeActionConfig?) } // Internal class handling all actions in datasource class SKSwipeActionConfig { private var actionValues: [(title: String?, image: UIImage?, backGroundColor: UIColor, identifier: String)]? private var swipeActionConfig: UISwipeActionsConfiguration? public var didSelectAction: ((_ identifer: String) -> Void)? init(_ _actions: [(title: String?, image: UIImage?, backGroundColor: UIColor, identifier: String)]) { actionValues = _actions var skActions = [UIContextualAction]() actionValues?.forEach({ (item) in let action = UIContextualAction(style: .normal, title: item.title, handler: { [weak self] (action, view, handler) in handler(true) self?.didSelectAction?(item.identifier) }) action.backgroundColor = item.backGroundColor action.image = item.image skActions.append(action) }) swipeActionConfig = UISwipeActionsConfiguration(actions: skActions) swipeActionConfig?.performsFirstActionWithFullSwipe = true } func getConfig() -> UISwipeActionsConfiguration? { return swipeActionConfig } }
dataSource?.editActionForRow = { (indexPath, identifier, action) in switch action { case .Leading: print("Leading action tapped", "Identifer: \(identifier)") // do anything according to identifer if multiple actions case .Trailing: print("Trailing action tapped", "Identifer: \(identifier)") // do anything according to identifer if multiple actions } }