diff --git a/Tablet/TableDirector.swift b/Tablet/TableDirector.swift new file mode 100644 index 0000000..5e81f42 --- /dev/null +++ b/Tablet/TableDirector.swift @@ -0,0 +1,176 @@ +// +// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import UIKit +import Foundation + +/** + Responsible for table view's datasource and delegate. + */ +public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate { + + private weak var tableView: UITableView! + private var sections = [TableSectionBuilder]() + + public init(tableView: UITableView) { + super.init() + + self.tableView = tableView + self.tableView.delegate = self + self.tableView.dataSource = self + + NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReceiveAction:", name: kActionPerformedNotificationKey, object: nil) + } + + deinit { + + NSNotificationCenter.defaultCenter().removeObserver(self) + } + + // MARK: Public methods + + public func appendSection(section: TableSectionBuilder) { + sections.append(section) + } + + public func appendSections(sections: [TableSectionBuilder]) { + self.sections.appendContentsOf(sections) + } + + // 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) + */ + private func builderAtIndexPath(indexPath: NSIndexPath) -> (RowBuilder, Int) { + + return sections[indexPath.section].builderAtIndex(indexPath.row)! + } + + private func triggerAction(action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath) -> AnyObject? { + + let builder = builderAtIndexPath(indexPath) + return builder.0.triggerAction(action.rawValue, cell: cell, indexPath: indexPath, itemIndex: builder.1) + } + + internal func didReceiveAction(notification: NSNotification) { + + if let action = notification.object as? Action, indexPath = tableView.indexPathForCell(action.cell) { + + let builder = builderAtIndexPath(indexPath) + builder.0.triggerAction(action.key, cell: action.cell, indexPath: indexPath, itemIndex: builder.1) + } + } + + // MARK: UITableViewDataSource - configuration + + public func numberOfSectionsInTableView(tableView: UITableView) -> Int { + + return sections.count + } + + public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + + return sections[section].numberOfRowsInSection + } + + public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + + let builder = builderAtIndexPath(indexPath) + + let cell = tableView.dequeueReusableCellWithIdentifier(builder.0.reusableIdentifier, forIndexPath: indexPath) + + builder.0.triggerAction(ActionType.configure.rawValue, cell: cell, indexPath: indexPath, itemIndex: builder.1) + + return cell + } + + // MARK: UITableViewDataSource - section setup + + public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + + return sections[section].headerTitle + } + + public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { + + return sections[section].footerTitle + } + + // MARK: UITableViewDelegate - section setup + + public func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + + return sections[section].headerView + } + + public func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { + + return sections[section].footerView + } + + public func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { + + return sections[section].headerHeight + } + + public func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + + return sections[section].footerHeight + } + + // MARK: UITableViewDelegate - actions + + public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + + let cell = tableView.cellForRowAtIndexPath(indexPath) + + if triggerAction(.click, cell: cell, indexPath: indexPath) != nil { + tableView.deselectRowAtIndexPath(indexPath, animated: true) + } else { + triggerAction(.select, cell: cell, indexPath: indexPath) + } + } + + public func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) { + + triggerAction(.deselect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) + } + + public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { + + triggerAction(.willDisplay, cell: cell, indexPath: indexPath) + } + + public func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool { + + return triggerAction(.shouldHighlight, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? true + } + + public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { + + return triggerAction(.height, cell: nil, indexPath: indexPath) as? CGFloat ?? tableView.rowHeight + } +} \ No newline at end of file diff --git a/Tablet/TableSectionBuilder.swift b/Tablet/TableSectionBuilder.swift index ac58e38..4853b3e 100644 --- a/Tablet/TableSectionBuilder.swift +++ b/Tablet/TableSectionBuilder.swift @@ -23,6 +23,7 @@ import Foundation /** Responsible for building a certain table view section. + Can host several row builders. */ public class TableSectionBuilder { diff --git a/Tablet/Tablet.swift b/Tablet/Tablet.swift index 00035e0..d9fbf9e 100644 --- a/Tablet/Tablet.swift +++ b/Tablet/Tablet.swift @@ -103,158 +103,4 @@ public protocol RowBuilder { var reusableIdentifier: String { get } func triggerAction(key: String, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int) -> AnyObject? -} - -/** - Responsible for table view's datasource and delegate. -*/ -public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate { - - private weak var tableView: UITableView! - private var sections = [TableSectionBuilder]() - - public init(tableView: UITableView) { - super.init() - - self.tableView = tableView - self.tableView.delegate = self - self.tableView.dataSource = self - - NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReceiveAction:", name: kActionPerformedNotificationKey, object: nil) - } - - deinit { - - NSNotificationCenter.defaultCenter().removeObserver(self) - } - - // MARK: Public methods - - public func appendSection(section: TableSectionBuilder) { - sections.append(section) - } - - public func appendSections(sections: [TableSectionBuilder]) { - self.sections.appendContentsOf(sections) - } - - // 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) - */ - private func builderAtIndexPath(indexPath: NSIndexPath) -> (RowBuilder, Int) { - - return sections[indexPath.section].builderAtIndex(indexPath.row)! - } - - private func triggerAction(action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath) -> AnyObject? { - - let builder = builderAtIndexPath(indexPath) - return builder.0.triggerAction(action.rawValue, cell: cell, indexPath: indexPath, itemIndex: builder.1) - } - - internal func didReceiveAction(notification: NSNotification) { - - if let action = notification.object as? Action, indexPath = tableView.indexPathForCell(action.cell) { - - let builder = builderAtIndexPath(indexPath) - builder.0.triggerAction(action.key, cell: action.cell, indexPath: indexPath, itemIndex: builder.1) - } - } - - // MARK: UITableViewDataSource - configuration - - public func numberOfSectionsInTableView(tableView: UITableView) -> Int { - - return sections.count - } - - public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - - return sections[section].numberOfRowsInSection - } - - public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { - - let builder = builderAtIndexPath(indexPath) - - let cell = tableView.dequeueReusableCellWithIdentifier(builder.0.reusableIdentifier, forIndexPath: indexPath) - - builder.0.triggerAction(ActionType.configure.rawValue, cell: cell, indexPath: indexPath, itemIndex: builder.1) - - return cell - } - - // MARK: UITableViewDataSource - section setup - - public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - - return sections[section].headerTitle - } - - public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { - - return sections[section].footerTitle - } - - // MARK: UITableViewDelegate - section setup - - public func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - - return sections[section].headerView - } - - public func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { - - return sections[section].footerView - } - - public func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - - return sections[section].headerHeight - } - - public func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { - - return sections[section].footerHeight - } - - // MARK: UITableViewDelegate - actions - - public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { - - let cell = tableView.cellForRowAtIndexPath(indexPath) - - if triggerAction(.click, cell: cell, indexPath: indexPath) != nil { - tableView.deselectRowAtIndexPath(indexPath, animated: true) - } else { - triggerAction(.select, cell: cell, indexPath: indexPath) - } - } - - public func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) { - - triggerAction(.deselect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) - } - - public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { - - triggerAction(.willDisplay, cell: cell, indexPath: indexPath) - } - - public func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool { - - return triggerAction(.shouldHighlight, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? true - } - - public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { - - return triggerAction(.height, cell: nil, indexPath: indexPath) as? CGFloat ?? tableView.rowHeight - } } \ No newline at end of file diff --git a/TabletDemo/TabletDemo.xcodeproj/project.pbxproj b/TabletDemo/TabletDemo.xcodeproj/project.pbxproj index 9f09f6c..42969fb 100644 --- a/TabletDemo/TabletDemo.xcodeproj/project.pbxproj +++ b/TabletDemo/TabletDemo.xcodeproj/project.pbxproj @@ -7,20 +7,22 @@ objects = { /* Begin PBXBuildFile section */ - 508B71841BF48DD300272920 /* TableSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508B71831BF48DD300272920 /* TableSectionBuilder.swift */; settings = {ASSET_TAGS = (); }; }; - 508B71861BF48E0D00272920 /* TableRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508B71851BF48E0D00272920 /* TableRowBuilder.swift */; settings = {ASSET_TAGS = (); }; }; + 508B71841BF48DD300272920 /* TableSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508B71831BF48DD300272920 /* TableSectionBuilder.swift */; }; + 508B71861BF48E0D00272920 /* TableRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508B71851BF48E0D00272920 /* TableRowBuilder.swift */; }; + DA1BCD0F1BF5472C00CC0479 /* TableDirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */; }; DAB7EB2B1BEF787300D2AD5E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB2A1BEF787300D2AD5E /* AppDelegate.swift */; }; DAB7EB2D1BEF787300D2AD5E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */; }; DAB7EB301BEF787300D2AD5E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */; }; DAB7EB321BEF787300D2AD5E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAB7EB311BEF787300D2AD5E /* Assets.xcassets */; }; DAB7EB351BEF787300D2AD5E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */; }; - DAB7EB3E1BEF78A400D2AD5E /* Tablet.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */; settings = {ASSET_TAGS = (); }; }; - DAB7EB401BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */; settings = {ASSET_TAGS = (); }; }; + DAB7EB3E1BEF78A400D2AD5E /* Tablet.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */; }; + DAB7EB401BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 508B71831BF48DD300272920 /* TableSectionBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSectionBuilder.swift; sourceTree = ""; }; 508B71851BF48E0D00272920 /* TableRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowBuilder.swift; sourceTree = ""; }; + DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = ""; }; DAB7EB271BEF787300D2AD5E /* TabletDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TabletDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; DAB7EB2A1BEF787300D2AD5E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -78,8 +80,9 @@ isa = PBXGroup; children = ( DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */, - 508B71831BF48DD300272920 /* TableSectionBuilder.swift */, + DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */, 508B71851BF48E0D00272920 /* TableRowBuilder.swift */, + 508B71831BF48DD300272920 /* TableSectionBuilder.swift */, ); name = Tablet; path = ../Tablet; @@ -159,6 +162,7 @@ DAB7EB2D1BEF787300D2AD5E /* ViewController.swift in Sources */, DAB7EB3E1BEF78A400D2AD5E /* Tablet.swift in Sources */, 508B71861BF48E0D00272920 /* TableRowBuilder.swift in Sources */, + DA1BCD0F1BF5472C00CC0479 /* TableDirector.swift in Sources */, DAB7EB401BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift in Sources */, DAB7EB2B1BEF787300D2AD5E /* AppDelegate.swift in Sources */, ); diff --git a/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate index 4169e6e..0a16516 100644 Binary files a/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate and b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TabletDemo/TabletDemo/ViewController.swift b/TabletDemo/TabletDemo/ViewController.swift index 848d2a2..842f30e 100644 --- a/TabletDemo/TabletDemo/ViewController.swift +++ b/TabletDemo/TabletDemo/ViewController.swift @@ -19,7 +19,7 @@ class ViewController: UIViewController { tableDirector = TableDirector(tableView: tableView) let rowBuilder = TableRowBuilder(items: [1, 2, 3, 4], id: "cell") - .action(.configure) { data -> Void in + .action(.configure) { data in data.cell?.textLabel?.text = "\(data.item)" } @@ -31,18 +31,18 @@ class ViewController: UIViewController { return false } - + let configurableRowBuilder = TableConfigurableRowBuilder(items: ["5", "6", "7", "8"]) - .action(kConfigurableTableViewCellButtonClickedAction) { data -> Void in - - print("custom action indexPath: \(data.indexPath), item: \(data.item)") - } .action(.click) { data -> Void in data.cell!.textLabel?.text = "" print("custom action indexPath: \(data.indexPath), item: \(data.item)") } + .action(kConfigurableTableViewCellButtonClickedAction) { data in + + print("custom action indexPath: \(data.indexPath), item: \(data.item)") + } let sectionBuilder = TableSectionBuilder(headerTitle: "Tablet", footerTitle: "Deal with table view like a boss.", rowBuilders: [rowBuilder, configurableRowBuilder])