support can edit
This commit is contained in:
parent
b5f7bd25e6
commit
b1f3b110b1
|
|
@ -22,10 +22,10 @@ import UIKit
|
|||
import Foundation
|
||||
|
||||
/**
|
||||
Responsible for table view's datasource and delegate.
|
||||
Responsible for table view's datasource and delegate.
|
||||
*/
|
||||
public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
||||
|
||||
|
||||
public private(set) weak var tableView: UITableView!
|
||||
private var sections = [TableSectionBuilder]()
|
||||
public weak var scrollDelegate: UIScrollViewDelegate?
|
||||
|
|
@ -36,7 +36,7 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
|
|||
self.tableView = tableView
|
||||
self.tableView.delegate = self
|
||||
self.tableView.dataSource = self
|
||||
|
||||
|
||||
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(didReceiveAction), name: kActionPerformedNotificationKey, object: nil)
|
||||
}
|
||||
|
||||
|
|
@ -44,17 +44,17 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
|
|||
|
||||
NSNotificationCenter.defaultCenter().removeObserver(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private methods
|
||||
|
||||
/**
|
||||
Find a row builder that responsible for building a row from cell with given item type.
|
||||
|
||||
- Parameters:
|
||||
- indexPath: path of cell to dequeue
|
||||
|
||||
- Returns: A touple - (builder, builderItemIndex)
|
||||
*/
|
||||
Find a row builder that responsible for building a row from cell with given item type.
|
||||
|
||||
- Parameters:
|
||||
- indexPath: path of cell to dequeue
|
||||
|
||||
- Returns: A touple - (builder, builderItemIndex)
|
||||
*/
|
||||
private func builderAtIndexPath(indexPath: NSIndexPath) -> (RowBuilder, Int) {
|
||||
|
||||
return sections[indexPath.section].builderAtIndex(indexPath.row)!
|
||||
|
|
@ -74,18 +74,18 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
|
|||
builder.0.invokeAction(.custom(action.key), cell: action.cell, indexPath: indexPath, itemIndex: builder.1, userInfo: action.userInfo)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override func respondsToSelector(selector: Selector) -> Bool {
|
||||
return super.respondsToSelector(selector) || scrollDelegate?.respondsToSelector(selector) == true
|
||||
}
|
||||
|
||||
|
||||
public override func forwardingTargetForSelector(selector: Selector) -> AnyObject? {
|
||||
return scrollDelegate?.respondsToSelector(selector) == true ? scrollDelegate : super.forwardingTargetForSelector(selector)
|
||||
}
|
||||
}
|
||||
|
||||
public extension TableDirector {
|
||||
|
||||
|
||||
// MARK: UITableViewDataSource - configuration
|
||||
|
||||
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
|
||||
|
|
@ -103,7 +103,7 @@ public extension TableDirector {
|
|||
let builder = builderAtIndexPath(indexPath)
|
||||
|
||||
let cell = tableView.dequeueReusableCellWithIdentifier(builder.0.reusableIdentifier, forIndexPath: indexPath)
|
||||
|
||||
|
||||
if cell.frame.size.width != tableView.frame.size.width {
|
||||
cell.frame = CGRectMake(0, 0, tableView.frame.size.width, cell.frame.size.height)
|
||||
cell.layoutIfNeeded()
|
||||
|
|
@ -116,7 +116,7 @@ public extension TableDirector {
|
|||
}
|
||||
|
||||
public extension TableDirector {
|
||||
|
||||
|
||||
// MARK: UITableViewDataSource - section setup
|
||||
|
||||
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
|
|
@ -153,16 +153,16 @@ public extension TableDirector {
|
|||
}
|
||||
|
||||
public extension TableDirector {
|
||||
|
||||
|
||||
// MARK: UITableViewDelegate - actions
|
||||
|
||||
|
||||
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
|
||||
|
||||
|
||||
return builderAtIndexPath(indexPath).0.estimatedRowHeight
|
||||
}
|
||||
|
||||
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
|
||||
|
||||
|
||||
return invokeAction(.height, cell: nil, indexPath: indexPath) as? CGFloat ?? UITableViewAutomaticDimension
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ public extension TableDirector {
|
|||
}
|
||||
|
||||
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
|
||||
|
||||
|
||||
let cell = tableView.cellForRowAtIndexPath(indexPath)
|
||||
|
||||
if invokeAction(.click, cell: cell, indexPath: indexPath) != nil {
|
||||
|
|
@ -183,31 +183,31 @@ public extension TableDirector {
|
|||
}
|
||||
|
||||
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
|
||||
|
||||
|
||||
invokeAction(.deselect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath)
|
||||
}
|
||||
|
||||
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
|
||||
|
||||
|
||||
invokeAction(.willDisplay, cell: cell, indexPath: indexPath)
|
||||
}
|
||||
|
||||
func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
|
||||
|
||||
return invokeAction(.shouldHighlight, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? true
|
||||
}
|
||||
}
|
||||
|
||||
public extension TableDirector {
|
||||
|
||||
|
||||
// MARK: Sections manipulation
|
||||
|
||||
|
||||
public func appendSection(section: TableSectionBuilder) {
|
||||
appendSections([section])
|
||||
}
|
||||
|
||||
public func appendSections(sections: [TableSectionBuilder]) {
|
||||
|
||||
|
||||
sections.forEach { $0.willMoveToDirector(tableView) }
|
||||
self.sections.appendContentsOf(sections)
|
||||
}
|
||||
|
|
@ -217,8 +217,30 @@ public extension TableDirector {
|
|||
}
|
||||
}
|
||||
|
||||
public func +=(left: TableDirector, right: RowBuilder) {
|
||||
public extension TableDirector {
|
||||
|
||||
public func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
|
||||
return invokeAction(.canEdit, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? false
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
|
||||
|
||||
if editingStyle == .Delete {
|
||||
|
||||
invokeAction(.clickDelete, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath)
|
||||
|
||||
let builderInfo = builderAtIndexPath(indexPath)
|
||||
builderInfo.0.removeItemAtIndex(builderInfo.1)
|
||||
|
||||
tableView.beginUpdates()
|
||||
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
|
||||
tableView.endUpdates()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func +=(left: TableDirector, right: RowBuilder) {
|
||||
|
||||
left.appendSection(TableSectionBuilder(rowBuilders: [right]))
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +250,7 @@ public func +=(left: TableDirector, right: [RowBuilder]) {
|
|||
}
|
||||
|
||||
public func +=(left: TableDirector, right: TableSectionBuilder) {
|
||||
|
||||
|
||||
left.appendSection(right)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,12 +24,12 @@ import Foundation
|
|||
public typealias ReturnValue = AnyObject?
|
||||
|
||||
internal enum ActionHandler<I, C> {
|
||||
|
||||
|
||||
case actionBlock((data: ActionData<I, C>) -> Void)
|
||||
case actionReturnBlock((data: ActionData<I, C>) -> AnyObject?)
|
||||
|
||||
func invoke(data: ActionData<I, C>) -> ReturnValue {
|
||||
|
||||
|
||||
switch (self) {
|
||||
case .actionBlock(let closure):
|
||||
closure(data: data)
|
||||
|
|
@ -41,13 +41,13 @@ internal enum ActionHandler<I, C> {
|
|||
}
|
||||
|
||||
/**
|
||||
Responsible for building cells of given type and passing items to them.
|
||||
*/
|
||||
Responsible for building cells of given type and passing items to them.
|
||||
*/
|
||||
public class TableRowBuilder<I, C where C: UITableViewCell> : RowBuilder {
|
||||
|
||||
|
||||
private var actions = Dictionary<String, ActionHandler<I, C>>()
|
||||
public var items = [I]()
|
||||
|
||||
|
||||
public var reusableIdentifier: String
|
||||
public var estimatedRowHeight: CGFloat
|
||||
public var numberOfRows: Int {
|
||||
|
|
@ -64,7 +64,7 @@ public class TableRowBuilder<I, C where C: UITableViewCell> : RowBuilder {
|
|||
}
|
||||
|
||||
public init(items: [I]? = nil, id: String? = nil, estimatedRowHeight: CGFloat = 48) {
|
||||
|
||||
|
||||
reusableIdentifier = id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? ""
|
||||
self.estimatedRowHeight = estimatedRowHeight
|
||||
|
||||
|
|
@ -74,21 +74,21 @@ public class TableRowBuilder<I, C where C: UITableViewCell> : RowBuilder {
|
|||
}
|
||||
|
||||
// MARK: Chaining actions
|
||||
|
||||
|
||||
public func action(key: String, closure: (data: ActionData<I, C>) -> Void) -> Self {
|
||||
|
||||
|
||||
actions[key] = .actionBlock(closure)
|
||||
return self
|
||||
}
|
||||
|
||||
public func action(actionType: ActionType, closure: (data: ActionData<I, C>) -> Void) -> Self {
|
||||
|
||||
|
||||
actions[actionType.key] = .actionBlock(closure)
|
||||
return self
|
||||
}
|
||||
|
||||
public func action(actionType: ActionType, closure: (data: ActionData<I, C>) -> ReturnValue) -> Self {
|
||||
|
||||
|
||||
actions[actionType.key] = .actionReturnBlock(closure)
|
||||
return self
|
||||
}
|
||||
|
|
@ -96,49 +96,49 @@ public class TableRowBuilder<I, C where C: UITableViewCell> : RowBuilder {
|
|||
// MARK: Triggers
|
||||
|
||||
public func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]? = nil) -> AnyObject? {
|
||||
|
||||
|
||||
if let action = actions[actionType.key] {
|
||||
return action.invoke(ActionData(cell: cell as? C, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
public func registerCell(inTableView tableView: UITableView) {
|
||||
|
||||
|
||||
if tableView.dequeueReusableCellWithIdentifier(reusableIdentifier) != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
guard let resource = NSStringFromClass(C).componentsSeparatedByString(".").last else { return }
|
||||
|
||||
|
||||
let bundle = NSBundle(forClass: C.self)
|
||||
|
||||
if let _ = bundle.pathForResource(resource, ofType: "nib") { // existing cell
|
||||
|
||||
|
||||
tableView.registerNib(UINib(nibName: resource, bundle: bundle), forCellReuseIdentifier: reusableIdentifier)
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
tableView.registerClass(C.self, forCellReuseIdentifier: reusableIdentifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Responsible for building configurable cells of given type and passing items to them.
|
||||
*/
|
||||
Responsible for building configurable cells of given type and passing items to them.
|
||||
*/
|
||||
public class TableConfigurableRowBuilder<I, C: ConfigurableCell where C.Item == I, C: UITableViewCell> : TableRowBuilder<I, C> {
|
||||
|
||||
|
||||
public init(item: I, estimatedRowHeight: CGFloat = 48) {
|
||||
super.init(item: item, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight)
|
||||
}
|
||||
|
||||
|
||||
public init(items: [I]? = nil, estimatedRowHeight: CGFloat = 48) {
|
||||
super.init(items: items, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight)
|
||||
}
|
||||
|
||||
|
||||
public override func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]? = nil) -> AnyObject? {
|
||||
|
||||
|
||||
switch actionType {
|
||||
case .configure:
|
||||
(cell as? C)?.configureWithItem(items[itemIndex])
|
||||
|
|
@ -149,9 +149,16 @@ public class TableConfigurableRowBuilder<I, C: ConfigurableCell where C.Item ==
|
|||
}
|
||||
|
||||
public extension TableRowBuilder {
|
||||
|
||||
|
||||
// MARK: Items manipulation
|
||||
|
||||
public func removeItemAtIndex(index: Int) {
|
||||
|
||||
if index < items.count {
|
||||
items.removeAtIndex(index)
|
||||
}
|
||||
}
|
||||
|
||||
public func appendItems(items: [I]) {
|
||||
|
||||
self.items.appendContentsOf(items)
|
||||
|
|
@ -169,6 +176,6 @@ public func +=<I, C>(left: TableRowBuilder<I, C>, right: I) {
|
|||
}
|
||||
|
||||
public func +=<I, C>(left: TableRowBuilder<I, C>, right: [I]) {
|
||||
|
||||
|
||||
left.appendItems(right)
|
||||
}
|
||||
|
|
@ -24,10 +24,10 @@ import Foundation
|
|||
internal let kActionPerformedNotificationKey = "_action"
|
||||
|
||||
/**
|
||||
The actions that Tablet provides.
|
||||
*/
|
||||
The actions that Tablet provides.
|
||||
*/
|
||||
public enum ActionType {
|
||||
|
||||
|
||||
case click
|
||||
case select
|
||||
case deselect
|
||||
|
|
@ -36,10 +36,12 @@ public enum ActionType {
|
|||
case willDisplay
|
||||
case shouldHighlight
|
||||
case height
|
||||
case canEdit
|
||||
case clickDelete
|
||||
case custom(String)
|
||||
|
||||
|
||||
var key: String {
|
||||
|
||||
|
||||
switch (self) {
|
||||
case .custom(let key):
|
||||
return key
|
||||
|
|
@ -50,7 +52,7 @@ public enum ActionType {
|
|||
}
|
||||
|
||||
public class ActionData<I, C> {
|
||||
|
||||
|
||||
public let cell: C?
|
||||
public let item: I
|
||||
public let itemIndex: Int
|
||||
|
|
@ -66,63 +68,65 @@ public class ActionData<I, C> {
|
|||
}
|
||||
|
||||
/**
|
||||
A custom action that you can trigger from your cell.
|
||||
You can eacily catch actions using a chaining manner with your row builder.
|
||||
*/
|
||||
A custom action that you can trigger from your cell.
|
||||
You can eacily catch actions using a chaining manner with your row builder.
|
||||
*/
|
||||
public class Action {
|
||||
|
||||
|
||||
/// The cell that triggers an action.
|
||||
public let cell: UITableViewCell
|
||||
|
||||
|
||||
/// The action unique key.
|
||||
public let key: String
|
||||
|
||||
|
||||
/// The custom user info.
|
||||
public let userInfo: [NSObject: AnyObject]?
|
||||
|
||||
|
||||
public init(key: String, sender: UITableViewCell, userInfo: [NSObject: AnyObject]? = nil) {
|
||||
|
||||
|
||||
self.key = key
|
||||
self.cell = sender
|
||||
self.userInfo = userInfo
|
||||
}
|
||||
|
||||
|
||||
public func invoke() {
|
||||
|
||||
|
||||
NSNotificationCenter.defaultCenter().postNotificationName(kActionPerformedNotificationKey, object: self)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
If you want to delegate your cell configuration logic to cell itself (with your view model or even model) than
|
||||
just provide an implementation of this protocol for your cell. Enjoy safe-typisation.
|
||||
*/
|
||||
If you want to delegate your cell configuration logic to cell itself (with your view model or even model) than
|
||||
just provide an implementation of this protocol for your cell. Enjoy safe-typisation.
|
||||
*/
|
||||
public protocol ConfigurableCell {
|
||||
|
||||
|
||||
associatedtype Item
|
||||
|
||||
|
||||
static func reusableIdentifier() -> String
|
||||
func configureWithItem(item: Item)
|
||||
}
|
||||
|
||||
public extension ConfigurableCell where Self: UITableViewCell {
|
||||
|
||||
|
||||
static func reusableIdentifier() -> String {
|
||||
|
||||
|
||||
return NSStringFromClass(self).componentsSeparatedByString(".").last ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A protocol that every row builder should follow.
|
||||
A certain section can only works with row builders that respect this protocol.
|
||||
*/
|
||||
A protocol that every row builder should follow.
|
||||
A certain section can only works with row builders that respect this protocol.
|
||||
*/
|
||||
public protocol RowBuilder {
|
||||
|
||||
|
||||
var numberOfRows: Int { get }
|
||||
var reusableIdentifier: String { get }
|
||||
var estimatedRowHeight: CGFloat { get }
|
||||
|
||||
|
||||
func removeItemAtIndex(index: Int)
|
||||
|
||||
func registerCell(inTableView tableView: UITableView)
|
||||
func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject?
|
||||
}
|
||||
Binary file not shown.
Loading…
Reference in New Issue