diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..771048b --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +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. \ No newline at end of file diff --git a/README.md b/README.md index 240f65f..d23abe9 100644 --- a/README.md +++ b/README.md @@ -1 +1,177 @@ -# Tablet \ No newline at end of file +![Tablet](https://raw.githubusercontent.com/maxsokolov/tablet/assets/tablet.png) + +

+Swift 2 compatible +Platform iOS +CocoaPods compatible +License: MIT +

+ +Tablet is a super lightweight yet powerful generic library that handles a complexity of UITableView's datasource and delegate methods in a Swift environment. Tablet's goal is to provide an easiest way to create complex table views. With Tablet you don't have to write a messy code of `switch` or `if` statements when you deal with bunch of different cells in different sections. + +That's almost all you need in your controller to build a bunch of cells in a section: +```swift +TableConfigurableRowBuilder(items: ["1", "2", "3", "4", "5"], estimatedRowHeight: 42) +``` +Tablet respects cells reusability feature and it's type-safe. See the Usage section to learn more. + +## Requirements + +- iOS 8.0+ +- Xcode 7.1+ + +## Installation + +### CocoaPods +To integrate Tablet into your Xcode project using CocoaPods, specify it in your `Podfile`: + +```ruby +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +use_frameworks! + +pod 'Tablet' +``` + +Then, run the following command: + +```bash +$ pod install +``` + +## Usage + +### Very basic + +You may want to setup a very basic table view, without any custom cells. In that case simply use the `TableRowBuilder`. + +```swift +import Tablet + +let rowBuilder = TableRowBuilder(items: [user1, user2, user3], id: "reusable_id") + .action(.configure) { data in + + data.cell?.textLabel?.text = data.item.username + data.cell?.detailTextLabel?.text = data.item.isActive ? "Active" : "Inactive" + } + +let sectionBuilder = TableSectionBuilder(headerTitle: "Users", rowBuilders: [rowBuilder]) + +director = TableDirector(tableView: tableView) +director.appendSections(sectionBuilder) +``` + +### Type-safe configurable cells + +Let's say you want to put your cell configuration logic into cell itself. Say you want to pass your view model (or even model) to your cell. +You could easily do this using the `TableConfigurableRowBuilder`. Your cell should respect the `ConfigurableCell` protocol as you may see in example below: + +```swift +import Tablet + +class MyTableViewCell : UITableViewCell, ConfigurableCell { + + typealias Item = User + + static func reusableIdentifier() -> String { + return "reusable_id" + } + + func configureWithItem(item: Item) { // item is user here + + textLabel?.text = item.username + detailTextLabel?.text = item.isActive ? "Active" : "Inactive" + } +} +``` +Once you've implemented the protocol, simply use the `TableConfigurableRowBuilder` to build cells: + +```swift +import Tablet + +let rowBuilder = TableConfigurableRowBuilder(estimatedRowHeight: 42) +rowBuilder.appendItems(users) + +director = TableDirector(tableView: tableView) +tableDirector.appendSection(TableSectionBuilder(rowBuilders: [rowBuilder])) +``` + +### Cell actions + +Tablet provides a chaining approach to handle actions from your cells: + +```swift +import Tablet + +let rowBuilder = TableRowBuilder(items: [user1, user2, user3], id: "reusable_id") + .action(.configure) { data in + + } + .action(.click) { data in + + } + .action(.shouldHighlight) { data in + + return false + } +``` + +### Custom cell actions +```swift +import Tablet + +let kMyAction = "action_key" + +class MyTableViewCell : UITableViewCell { + + @IBAction func buttonClicked(sender: UIButton) { + + Action(key: kMyAction, sender: self, userInfo: nil).perform() + } +} +``` +And receive this actions with your row builder: +```swift +import Tablet + +let rowBuilder = TableConfigurableRowBuilder(items: users, id: "reusable_id", estimatedRowHeight: 42) + .action(.click) { data in + + } + .action(.willDisplay) { data in + + } + .action(kMyAction) { data in + + } +``` + +## Extensibility + +If you find that Tablet is not provide an action you need, for example you need UITableViewDelegate's `didEndDisplayingCell` method and it's not out of the box, +simply provide an extension for `TableDirector` as follow: +```swift +import Tablet + +let kTableDirectorDidEndDisplayingCell = "enddisplaycell" + +extension TableDirector { + + public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { + + performAction(.custom(kTableDirectorDidEndDisplayingCell), cell: cell, indexPath: indexPath) + } +} +``` +Catch your action with row builder: +```swift +let rowBuilder = TableConfigurableRowBuilder(items: users, estimatedRowHeight: 42) + .action(kTableDirectorDidEndDisplayingCell) { data in + + } +``` +You could also perform an action that returns a value. + +## License + +Tablet is available under the MIT license. See LICENSE for details. \ No newline at end of file diff --git a/Tablet.podspec b/Tablet.podspec new file mode 100644 index 0000000..de7a9f9 --- /dev/null +++ b/Tablet.podspec @@ -0,0 +1,16 @@ +Pod::Spec.new do |s| + s.name = 'Tablet' + s.version = '0.1.0' + + s.homepage = 'https://github.com/maxsokolov/tablet' + s.summary = 'Powerful type-safe tool for UITableView. Swift 2.0 is required.' + + s.author = { 'Max Sokolov' => 'i@maxsokolov.net' } + s.license = { :type => 'MIT', :file => 'LICENSE' } + s.platforms = { :ios => '8.0' } + s.ios.deployment_target = '8.0' + + s.source_files = 'Tablet/*.swift' + s.module_name = 'Tablet' + s.source = { :git => 'https://github.com/maxsokolov/tablet.git', :tag => s.version } +end \ No newline at end of file diff --git a/Tablet/TableDirector.swift b/Tablet/TableDirector.swift new file mode 100644 index 0000000..30c6daf --- /dev/null +++ b/Tablet/TableDirector.swift @@ -0,0 +1,187 @@ +// +// 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: Sections manipulation + + 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)! + } + + public func performAction(action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath) -> AnyObject? { + + let builder = builderAtIndexPath(indexPath) + return builder.0.performAction(action, 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.performAction(.custom(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.performAction(.configure, cell: cell, indexPath: indexPath, itemIndex: builder.1) + + return cell + } +} + +extension TableDirector { + + // 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 + } +} + +extension TableDirector { + + // MARK: UITableViewDelegate - actions + + public func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { + + return builderAtIndexPath(indexPath).0.estimatedRowHeight + } + + public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { + + return performAction(.height, cell: nil, indexPath: indexPath) as? CGFloat ?? UITableViewAutomaticDimension + } + + public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + + let cell = tableView.cellForRowAtIndexPath(indexPath) + + if performAction(.click, cell: cell, indexPath: indexPath) != nil { + tableView.deselectRowAtIndexPath(indexPath, animated: true) + } else { + performAction(.select, cell: cell, indexPath: indexPath) + } + } + + public func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) { + + performAction(.deselect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) + } + + public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { + + performAction(.willDisplay, cell: cell, indexPath: indexPath) + } + + public func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool { + + return performAction(.shouldHighlight, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? true + } +} \ No newline at end of file diff --git a/Tablet/TableRowBuilder.swift b/Tablet/TableRowBuilder.swift new file mode 100644 index 0000000..edd2854 --- /dev/null +++ b/Tablet/TableRowBuilder.swift @@ -0,0 +1,141 @@ +// +// 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 + +public typealias ReturnValue = AnyObject? + +internal enum ActionHandler { + + case actionBlock((data: ActionData) -> Void) + case actionReturnBlock((data: ActionData) -> AnyObject?) + + func call(data: ActionData) -> AnyObject? { + + switch (self) { + case .actionBlock(let closure): + closure(data: data) + return true + case .actionReturnBlock(let closure): + return closure(data: data) + } + } +} + +/** + Responsible for building cells of given type and passing items to them. +*/ +public class TableRowBuilder : RowBuilder { + + private var actions = Dictionary>() + private var items = [I]() + + public var reusableIdentifier: String + public var estimatedRowHeight: CGFloat + public var numberOfRows: Int { + get { + return items.count + } + } + + public init(item: I, id: String, estimatedRowHeight: CGFloat = UITableViewAutomaticDimension) { + + reusableIdentifier = id + self.estimatedRowHeight = estimatedRowHeight + items.append(item) + } + + public init(items: [I]? = nil, id: String, estimatedRowHeight: CGFloat = UITableViewAutomaticDimension) { + + reusableIdentifier = id + self.estimatedRowHeight = estimatedRowHeight + + if items != nil { + self.items.appendContentsOf(items!) + } + } + + // MARK: Items manipulation + + public func appendItems(items: [I]) { + + self.items.appendContentsOf(items) + } + + public func clear() { + + items.removeAll() + } + + // 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 + } + + // MARK: Triggers + + public func performAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int) -> AnyObject? { + + if let action = actions[actionType.key] { + return action.call(ActionData(cell: cell as? C, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex)) + } + return nil + } +} + +/** + Responsible for building configurable cells of given type and passing items to them. +*/ +public class TableConfigurableRowBuilder : TableRowBuilder { + + public init(item: I, estimatedRowHeight: CGFloat = UITableViewAutomaticDimension) { + super.init(item: item, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight) + } + + public init(items: [I]? = nil, estimatedRowHeight: CGFloat = UITableViewAutomaticDimension) { + super.init(items: items, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight) + } + + public override func performAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int) -> AnyObject? { + + switch actionType { + case .configure: + (cell as? C)?.configureWithItem(items[itemIndex]) + default: break + } + return super.performAction(actionType, cell: cell, indexPath: indexPath, itemIndex: itemIndex) + } +} diff --git a/Tablet/TableSectionBuilder.swift b/Tablet/TableSectionBuilder.swift new file mode 100644 index 0000000..4853b3e --- /dev/null +++ b/Tablet/TableSectionBuilder.swift @@ -0,0 +1,77 @@ +// +// 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 building a certain table view section. + Can host several row builders. +*/ +public class TableSectionBuilder { + + private var builders = [RowBuilder]() + + public var headerTitle: String? + public var footerTitle: String? + + public var headerView: UIView? + public var headerHeight: CGFloat = UITableViewAutomaticDimension + + public var footerView: UIView? + public var footerHeight: CGFloat = UITableViewAutomaticDimension + + /// A total number of rows in section of each row builder. + public var numberOfRowsInSection: Int { + + return builders.reduce(0) { $0 + $1.numberOfRows } + } + + public init(headerTitle: String? = nil, footerTitle: String? = nil, rowBuilders: [RowBuilder]? = nil) { + + self.headerTitle = headerTitle + self.footerTitle = footerTitle + + if let initialRows = rowBuilders { + self.builders.appendContentsOf(initialRows) + } + } + + public init(headerView: UIView? = nil, headerHeight: CGFloat = UITableViewAutomaticDimension, footerView: UIView? = nil, footerHeight: CGFloat = UITableViewAutomaticDimension) { + + self.headerView = headerView + self.headerHeight = headerHeight + + self.footerView = footerView + self.footerHeight = footerHeight + } + + internal func builderAtIndex(var index: Int) -> (RowBuilder, Int)? { + + for builder in builders { + if index < builder.numberOfRows { + return (builder, index) + } + index -= builder.numberOfRows + } + + return nil + } +} \ No newline at end of file diff --git a/Tablet/Tablet.swift b/Tablet/Tablet.swift new file mode 100644 index 0000000..3331c53 --- /dev/null +++ b/Tablet/Tablet.swift @@ -0,0 +1,118 @@ +// +// 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 + +internal let kActionPerformedNotificationKey = "_action" + +/** + The actions that Tablet provides. +*/ +public enum ActionType { + + case click + case select + case deselect + case configure + case willDisplay + case shouldHighlight + case height + case custom(String) + + var key: String { + + switch (self) { + case .custom(let key): + return key + default: + return "_\(self)" + } + } +} + +public class ActionData { + + public let cell: C? + public let item: I + public let itemIndex: Int + public let indexPath: NSIndexPath + + init(cell: C?, indexPath: NSIndexPath, item: I, itemIndex: Int) { + + self.cell = cell + self.indexPath = indexPath + self.item = item + self.itemIndex = itemIndex + } +} + +/** + 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 perform() { + + 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. +*/ +public protocol ConfigurableCell { + + typealias Item + + static func reusableIdentifier() -> String + func configureWithItem(item: Item) +} + +/** + 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 performAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int) -> AnyObject? +} \ No newline at end of file diff --git a/TabletDemo/TabletDemo.xcodeproj/project.pbxproj b/TabletDemo/TabletDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..f241c66 --- /dev/null +++ b/TabletDemo/TabletDemo.xcodeproj/project.pbxproj @@ -0,0 +1,333 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 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 */; }; + DA1BCD111BF7388C00CC0479 /* CustomTableActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BCD101BF7388C00CC0479 /* CustomTableActions.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 */; }; + 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 = ""; }; + DA1BCD101BF7388C00CC0479 /* CustomTableActions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTableActions.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 = ""; }; + DAB7EB2F1BEF787300D2AD5E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + DAB7EB311BEF787300D2AD5E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + DAB7EB341BEF787300D2AD5E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + DAB7EB361BEF787300D2AD5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tablet.swift; sourceTree = ""; }; + DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurableTableViewCell.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DAB7EB241BEF787300D2AD5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DAB7EB1E1BEF787300D2AD5E = { + isa = PBXGroup; + children = ( + DAB7EB3C1BEF789500D2AD5E /* Tablet */, + DAB7EB291BEF787300D2AD5E /* TabletDemo */, + DAB7EB281BEF787300D2AD5E /* Products */, + ); + sourceTree = ""; + }; + DAB7EB281BEF787300D2AD5E /* Products */ = { + isa = PBXGroup; + children = ( + DAB7EB271BEF787300D2AD5E /* TabletDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + DAB7EB291BEF787300D2AD5E /* TabletDemo */ = { + isa = PBXGroup; + children = ( + DAB7EB2A1BEF787300D2AD5E /* AppDelegate.swift */, + DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */, + DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */, + DA1BCD101BF7388C00CC0479 /* CustomTableActions.swift */, + DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */, + DAB7EB311BEF787300D2AD5E /* Assets.xcassets */, + DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */, + DAB7EB361BEF787300D2AD5E /* Info.plist */, + ); + path = TabletDemo; + sourceTree = ""; + }; + DAB7EB3C1BEF789500D2AD5E /* Tablet */ = { + isa = PBXGroup; + children = ( + DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */, + DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */, + 508B71851BF48E0D00272920 /* TableRowBuilder.swift */, + 508B71831BF48DD300272920 /* TableSectionBuilder.swift */, + ); + name = Tablet; + path = ../Tablet; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + DAB7EB261BEF787300D2AD5E /* TabletDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TabletDemo" */; + buildPhases = ( + DAB7EB231BEF787300D2AD5E /* Sources */, + DAB7EB241BEF787300D2AD5E /* Frameworks */, + DAB7EB251BEF787300D2AD5E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TabletDemo; + productName = TabletDemo; + productReference = DAB7EB271BEF787300D2AD5E /* TabletDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DAB7EB1F1BEF787300D2AD5E /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0700; + ORGANIZATIONNAME = Tablet; + TargetAttributes = { + DAB7EB261BEF787300D2AD5E = { + CreatedOnToolsVersion = 7.0.1; + DevelopmentTeam = Z48R734SJX; + }; + }; + }; + buildConfigurationList = DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TabletDemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = DAB7EB1E1BEF787300D2AD5E; + productRefGroup = DAB7EB281BEF787300D2AD5E /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DAB7EB261BEF787300D2AD5E /* TabletDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DAB7EB251BEF787300D2AD5E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DAB7EB351BEF787300D2AD5E /* LaunchScreen.storyboard in Resources */, + DAB7EB321BEF787300D2AD5E /* Assets.xcassets in Resources */, + DAB7EB301BEF787300D2AD5E /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + DAB7EB231BEF787300D2AD5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 508B71841BF48DD300272920 /* TableSectionBuilder.swift in Sources */, + 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 */, + DA1BCD111BF7388C00CC0479 /* CustomTableActions.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DAB7EB2F1BEF787300D2AD5E /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + DAB7EB341BEF787300D2AD5E /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + DAB7EB371BEF787300D2AD5E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + DAB7EB381BEF787300D2AD5E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + DAB7EB3A1BEF787300D2AD5E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + INFOPLIST_FILE = TabletDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.tablet.TabletDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + }; + name = Debug; + }; + DAB7EB3B1BEF787300D2AD5E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + INFOPLIST_FILE = TabletDemo/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.tablet.TabletDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TabletDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DAB7EB371BEF787300D2AD5E /* Debug */, + DAB7EB381BEF787300D2AD5E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TabletDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DAB7EB3A1BEF787300D2AD5E /* Debug */, + DAB7EB3B1BEF787300D2AD5E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = DAB7EB1F1BEF787300D2AD5E /* Project object */; +} diff --git a/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..9fbb493 --- /dev/null +++ b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..72a945c Binary files /dev/null and b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..37a26d5 Binary files /dev/null and b/TabletDemo/TabletDemo.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TabletDemo/TabletDemo.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/TabletDemo.xcscheme b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/TabletDemo.xcscheme new file mode 100644 index 0000000..6db4123 --- /dev/null +++ b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/TabletDemo.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TabletDemo/TabletDemo.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..c382e1a --- /dev/null +++ b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/max.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + TabletDemo.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + DAB7EB261BEF787300D2AD5E + + primary + + + + + diff --git a/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..fe2b454 --- /dev/null +++ b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/TabletDemo.xcscheme b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/TabletDemo.xcscheme new file mode 100644 index 0000000..6db4123 --- /dev/null +++ b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/TabletDemo.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/xcschememanagement.plist b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..c382e1a --- /dev/null +++ b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + TabletDemo.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + DAB7EB261BEF787300D2AD5E + + primary + + + + + diff --git a/TabletDemo/TabletDemo/AppDelegate.swift b/TabletDemo/TabletDemo/AppDelegate.swift new file mode 100644 index 0000000..69cc0e6 --- /dev/null +++ b/TabletDemo/TabletDemo/AppDelegate.swift @@ -0,0 +1,15 @@ +// +// AppDelegate.swift +// TabletDemo +// +// Created by Max Sokolov on 08/11/15. +// Copyright © 2015 Tablet. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? +} \ No newline at end of file diff --git a/TabletDemo/TabletDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/TabletDemo/TabletDemo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..118c98f --- /dev/null +++ b/TabletDemo/TabletDemo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/TabletDemo/TabletDemo/Base.lproj/LaunchScreen.storyboard b/TabletDemo/TabletDemo/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..ffacbd6 --- /dev/null +++ b/TabletDemo/TabletDemo/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TabletDemo/TabletDemo/Base.lproj/Main.storyboard b/TabletDemo/TabletDemo/Base.lproj/Main.storyboard new file mode 100644 index 0000000..2bd0123 --- /dev/null +++ b/TabletDemo/TabletDemo/Base.lproj/Main.storyboard @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TabletDemo/TabletDemo/ConfigurableTableViewCell.swift b/TabletDemo/TabletDemo/ConfigurableTableViewCell.swift new file mode 100644 index 0000000..07be0a2 --- /dev/null +++ b/TabletDemo/TabletDemo/ConfigurableTableViewCell.swift @@ -0,0 +1,34 @@ +// +// ConfigurableTableViewCell.swift +// TabletDemo +// +// Created by Max Sokolov on 08/11/15. +// Copyright © 2015 Tablet. All rights reserved. +// + +import UIKit + +let kConfigurableTableViewCellButtonClickedAction = "button_clicked" + +class ConfigurableTableViewCell: UITableViewCell, ConfigurableCell { + + typealias Item = String + + @IBOutlet weak var button: UIButton! + @IBOutlet weak var contentLabel: UILabel! + + static func reusableIdentifier() -> String { + + return "configurable_cell" + } + + func configureWithItem(item: Item) { + + button.setTitle("Button \(item)", forState: .Normal) + } + + @IBAction func buttonClicked(sender: UIButton) { + + Action(key: kConfigurableTableViewCellButtonClickedAction, sender: self).perform() + } +} \ No newline at end of file diff --git a/TabletDemo/TabletDemo/CustomTableActions.swift b/TabletDemo/TabletDemo/CustomTableActions.swift new file mode 100644 index 0000000..c586a75 --- /dev/null +++ b/TabletDemo/TabletDemo/CustomTableActions.swift @@ -0,0 +1,20 @@ +// +// CustomTableActions.swift +// TabletDemo +// +// Created by Max Sokolov on 14/11/15. +// Copyright © 2015 Tablet. All rights reserved. +// + +import UIKit +import Foundation + +let kTableDirectorDidEndDisplayingCell = "enddisplaycell" + +extension TableDirector { + + public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { + + performAction(.custom(kTableDirectorDidEndDisplayingCell), cell: cell, indexPath: indexPath) + } +} \ No newline at end of file diff --git a/TabletDemo/TabletDemo/Info.plist b/TabletDemo/TabletDemo/Info.plist new file mode 100644 index 0000000..6c48029 --- /dev/null +++ b/TabletDemo/TabletDemo/Info.plist @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + + diff --git a/TabletDemo/TabletDemo/ViewController.swift b/TabletDemo/TabletDemo/ViewController.swift new file mode 100644 index 0000000..ed353d5 --- /dev/null +++ b/TabletDemo/TabletDemo/ViewController.swift @@ -0,0 +1,60 @@ +// +// ViewController.swift +// TabletDemo +// +// Created by Max Sokolov on 08/11/15. +// Copyright © 2015 Tablet. All rights reserved. +// + +import UIKit + +class ViewController: UIViewController { + + @IBOutlet weak var tableView: UITableView! + var tableDirector: TableDirector! + + override func viewDidLoad() { + super.viewDidLoad() + + tableDirector = TableDirector(tableView: tableView) + + let rowBuilder = TableRowBuilder(items: [1, 2, 3, 4], id: "cell") + .action(.configure) { data in + + data.cell?.textLabel?.text = "\(data.item)" + } + .action(.shouldHighlight) { data in + + return false + } + .action(kTableDirectorDidEndDisplayingCell) { data -> Void in + + print("end display: \(data.indexPath)") + } + + let configurableRowBuilder = TableConfigurableRowBuilder(items: ["5", "6", "7", "8"], estimatedRowHeight: 300) + .action(.click) { data -> Void in + + print("click action indexPath: \(data.indexPath), item: \(data.item)") + } + .action(kConfigurableTableViewCellButtonClickedAction) { data -> Void in + + print("custom action indexPath: \(data.indexPath), item: \(data.item)") + } + .action(.height) { data -> ReturnValue in + + if data.item == "5" { + return 70 + } + return nil + } + .action(.configure) { (data) -> Void in + + data.cell!.contentLabel.text = "Tablet is a super lightweight yet powerful generic library that handles a complexity of UITableView's datasource and delegate methods in a Swift environment. Tablet's goal is to provide an easiest way to create complex table views. With Tablet you don't have to write a messy code of switch or if statements when you deal with bunch of different cells in different sections." + } + + let sectionBuilder = TableSectionBuilder(headerTitle: "Tablet", footerTitle: "Deal with table view like a boss.", rowBuilders: [rowBuilder, configurableRowBuilder]) + + tableDirector.appendSection(sectionBuilder) + } +} \ No newline at end of file