diff --git a/Tablet/TableDirector.swift b/Tablet/TableDirector.swift index e8b64ee..f070027 100644 --- a/Tablet/TableDirector.swift +++ b/Tablet/TableDirector.swift @@ -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) } diff --git a/Tablet/TableRowBuilder.swift b/Tablet/TableRowBuilder.swift index a5b79f0..fd9fe52 100644 --- a/Tablet/TableRowBuilder.swift +++ b/Tablet/TableRowBuilder.swift @@ -24,12 +24,12 @@ import Foundation public typealias ReturnValue = AnyObject? internal enum ActionHandler { - + case actionBlock((data: ActionData) -> Void) case actionReturnBlock((data: ActionData) -> AnyObject?) func invoke(data: ActionData) -> ReturnValue { - + switch (self) { case .actionBlock(let closure): closure(data: data) @@ -41,13 +41,13 @@ internal enum ActionHandler { } /** - 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 : RowBuilder { - + private var actions = Dictionary>() public var items = [I]() - + public var reusableIdentifier: String public var estimatedRowHeight: CGFloat public var numberOfRows: Int { @@ -64,7 +64,7 @@ public class TableRowBuilder : 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 : RowBuilder { } // MARK: Chaining actions - + public func action(key: String, closure: (data: ActionData) -> Void) -> Self { - + actions[key] = .actionBlock(closure) return self } public func action(actionType: ActionType, closure: (data: ActionData) -> Void) -> Self { - + actions[actionType.key] = .actionBlock(closure) return self } public func action(actionType: ActionType, closure: (data: ActionData) -> ReturnValue) -> Self { - + actions[actionType.key] = .actionReturnBlock(closure) return self } @@ -96,49 +96,49 @@ public class TableRowBuilder : 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 : TableRowBuilder { - + 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(left: TableRowBuilder, right: I) { } public func +=(left: TableRowBuilder, right: [I]) { - + left.appendItems(right) } \ No newline at end of file diff --git a/Tablet/Tablet.swift b/Tablet/Tablet.swift index f0764e8..becc6f0 100644 --- a/Tablet/Tablet.swift +++ b/Tablet/Tablet.swift @@ -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 { - + public let cell: C? public let item: I public let itemIndex: Int @@ -66,63 +68,65 @@ public class ActionData { } /** - 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? } \ No newline at end of file diff --git a/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate index d52173a..a18bc63 100644 Binary files a/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate and b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate differ