diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..b570850 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: objective-c +osx_image: xcode7.2 +branches: + only: + - master +before_install: + - cd Tablet +script: + - xctool clean build test -project Tablet.xcodeproj -scheme Tablet -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 6' GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES +after_success: + - cd $TRAVIS_BUILD_DIR \ No newline at end of file diff --git a/README.md b/README.md index 84a933e..565e953 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,32 @@ -![Tablet](https://raw.githubusercontent.com/maxsokolov/tablet/assets/tablet.png) +#Tablet

+Build Status Swift 2 compatible Platform iOS -CocoaPods compatible +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: +## Features + +- [x] Type-safe cells based on generics +- [x] The easiest way to map your models or view models to cells +- [x] Correctly handles autolayout cells with multiline labels +- [x] Chainable cell actions +- [x] Support cells created from code, xib, or storyboard +- [x] Automatic xib/classes registration +- [x] No need to subclass +- [x] Extensibility +- [x] Tests + +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) +TableConfigurableRowBuilder(items: ["1", "2", "3", "4", "5"]) ``` -Tablet respects cells reusability feature and it's type-safe. See the Usage section to learn more. +Tablet respects cells reusability feature and built with performace in mind. See the Usage section to learn more. ## Requirements @@ -55,7 +68,7 @@ let rowBuilder = TableRowBuilder(items: [user1, user2, us data.cell?.detailTextLabel?.text = data.item.isActive ? "Active" : "Inactive" } -let sectionBuilder = TableSectionBuilder(headerTitle: "Users", rowBuilders: [rowBuilder]) +let sectionBuilder = TableSectionBuilder(headerTitle: "Users", rows: [rowBuilder]) director = TableDirector(tableView: tableView) director.appendSections(sectionBuilder) @@ -77,6 +90,10 @@ class MyTableViewCell : UITableViewCell, ConfigurableCell { return "reusable_id" } + static func estimatedHeight() -> Float { + return 255 + } + func configureWithItem(item: Item) { // item is user here textLabel?.text = item.username @@ -89,11 +106,11 @@ Once you've implemented the protocol, simply use the `TableConfigurableRowBuilde ```swift import Tablet -let rowBuilder = TableConfigurableRowBuilder(estimatedRowHeight: 42) +let rowBuilder = TableConfigurableRowBuilder() rowBuilder.appendItems(users) director = TableDirector(tableView: tableView) -tableDirector.appendSection(TableSectionBuilder(rowBuilders: [rowBuilder])) +tableDirector.appendSection(TableSectionBuilder(rows: [rowBuilder])) ``` ### Cell actions @@ -134,7 +151,7 @@ And receive this actions with your row builder: ```swift import Tablet -let rowBuilder = TableConfigurableRowBuilder(items: users, id: "reusable_id", estimatedRowHeight: 42) +let rowBuilder = TableConfigurableRowBuilder(items: users, id: "reusable_id") .action(.click) { data -> Void in } @@ -165,7 +182,7 @@ extension TableDirector { ``` Catch your action with row builder: ```swift -let rowBuilder = TableConfigurableRowBuilder(items: users, estimatedRowHeight: 42) +let rowBuilder = TableConfigurableRowBuilder(items: users) .action(kTableDirectorDidEndDisplayingCell) { data -> Void in } diff --git a/Tablet.podspec b/Tablet.podspec index 373dac0..c285709 100644 --- a/Tablet.podspec +++ b/Tablet.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'Tablet' - s.version = '0.2.6' + s.version = '0.3.0' s.homepage = 'https://github.com/maxsokolov/tablet' s.summary = 'Powerful type-safe tool for UITableView. Swift 2.0 is required.' diff --git a/Tablet.xcworkspace/contents.xcworkspacedata b/Tablet.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..8a2fad4 --- /dev/null +++ b/Tablet.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Tablet.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate b/Tablet.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..2abe584 Binary files /dev/null and b/Tablet.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Tablet/Info.plist b/Tablet/Info.plist new file mode 100644 index 0000000..d3de8ee --- /dev/null +++ b/Tablet/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSPrincipalClass + + + diff --git a/Tablet/TableDirector.swift b/Tablet/TableDirector.swift index 957b933..d50bd79 100644 --- a/Tablet/TableDirector.swift +++ b/Tablet/TableDirector.swift @@ -27,9 +27,9 @@ import Foundation public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate { public private(set) weak var tableView: UITableView! - private var sections = [TableSectionBuilder]() public weak var scrollDelegate: UIScrollViewDelegate? - + private var sections = [TableSectionBuilder]() + public init(tableView: UITableView) { super.init() @@ -37,7 +37,7 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate self.tableView.delegate = self self.tableView.dataSource = self - NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReceiveAction:", name: kActionPerformedNotificationKey, object: nil) + NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReceiveAction:", name: TabletNotifications.CellAction, object: nil) } deinit { @@ -56,24 +56,16 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate - Returns: A touple - (builder, builderItemIndex) */ private func builderAtIndexPath(indexPath: NSIndexPath) -> (RowBuilder, Int) { - return sections[indexPath.section].builderAtIndex(indexPath.row)! } + // MARK: Public + public func invokeAction(action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath) -> AnyObject? { let builder = builderAtIndexPath(indexPath) return builder.0.invokeAction(action, cell: cell, indexPath: indexPath, itemIndex: builder.1, userInfo: nil) } - - internal func didReceiveAction(notification: NSNotification) { - - if let action = notification.object as? Action, indexPath = tableView.indexPathForCell(action.cell) { - - let builder = builderAtIndexPath(indexPath) - 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 @@ -82,19 +74,28 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate public override func forwardingTargetForSelector(selector: Selector) -> AnyObject? { return scrollDelegate?.respondsToSelector(selector) == true ? scrollDelegate : super.forwardingTargetForSelector(selector) } + + // MARK: Internal + + func didReceiveAction(notification: NSNotification) { + + if let action = notification.object as? Action, indexPath = tableView.indexPathForCell(action.cell) { + + let builder = builderAtIndexPath(indexPath) + builder.0.invokeAction(.custom(action.key), cell: action.cell, indexPath: indexPath, itemIndex: builder.1, userInfo: notification.userInfo) + } + } } public extension TableDirector { - + // MARK: UITableViewDataSource - configuration func numberOfSectionsInTableView(tableView: UITableView) -> Int { - return sections.count } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return sections[section].numberOfRowsInSection } @@ -120,34 +121,28 @@ public extension TableDirector { // MARK: UITableViewDataSource - section setup func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - return sections[section].headerTitle } func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? { - return sections[section].footerTitle } // MARK: UITableViewDelegate - section setup func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - return sections[section].headerView } func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { - return sections[section].footerView } func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { - return sections[section].headerHeight } func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { - return sections[section].footerHeight } } @@ -157,19 +152,17 @@ public extension TableDirector { // MARK: UITableViewDelegate - actions func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { - - return builderAtIndexPath(indexPath).0.estimatedRowHeight + return CGFloat(builderAtIndexPath(indexPath).0.estimatedRowHeight) } func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { - return invokeAction(.height, cell: nil, indexPath: indexPath) as? CGFloat ?? UITableViewAutomaticDimension } - func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? { + /*func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? { - return invokeAction(.willSelect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? NSIndexPath ?? indexPath - } + return invokeAction(.willSelect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? NSIndexPath + }*/ func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { @@ -183,17 +176,14 @@ 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 } } @@ -218,21 +208,17 @@ public extension TableDirector { } public func +=(left: TableDirector, right: RowBuilder) { - - left.appendSection(TableSectionBuilder(rowBuilders: [right])) + left.appendSection(TableSectionBuilder(rows: [right])) } public func +=(left: TableDirector, right: [RowBuilder]) { - - left.appendSection(TableSectionBuilder(rowBuilders: right)) + left.appendSection(TableSectionBuilder(rows: right)) } public func +=(left: TableDirector, right: TableSectionBuilder) { - left.appendSection(right) } public func +=(left: TableDirector, right: [TableSectionBuilder]) { - left.appendSections(right) } \ No newline at end of file diff --git a/Tablet/TableRowBuilder.swift b/Tablet/TableRowBuilder.swift index 6a77c5c..fed1d6a 100644 --- a/Tablet/TableRowBuilder.swift +++ b/Tablet/TableRowBuilder.swift @@ -23,13 +23,13 @@ import Foundation public typealias ReturnValue = AnyObject? -internal enum ActionHandler { - +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) @@ -40,33 +40,50 @@ internal enum ActionHandler { } } -/** - 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 class RowBuilder : NSObject { + + public private(set) var reusableIdentifier: String public var numberOfRows: Int { - get { - return items.count - } + return 0 + } + public var estimatedRowHeight: Float { + return 44 } - public init(item: I, id: String? = nil, estimatedRowHeight: CGFloat = 48) { + init(id: String) { + reusableIdentifier = id + } + + // MARK: internal methods, must be overriden in subclass + + func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? { + return nil + } + + func registerCell(inTableView tableView: UITableView) { + } +} + +/** + 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 override var numberOfRows: Int { + return items.count + } + + public init(item: I, id: String? = nil) { + super.init(id: id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? "") - reusableIdentifier = id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? "" - self.estimatedRowHeight = estimatedRowHeight items.append(item) } - public init(items: [I]? = nil, id: String? = nil, estimatedRowHeight: CGFloat = 48) { - - reusableIdentifier = id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? "" - self.estimatedRowHeight = estimatedRowHeight + public init(items: [I]? = nil, id: String? = nil) { + super.init(id: id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? "") if items != nil { self.items.appendContentsOf(items!) @@ -74,101 +91,101 @@ 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 } - // MARK: Triggers + // MARK: Internal - public func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]? = nil) -> AnyObject? { - + override func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? { + if let action = actions[actionType.key] { - return action.invoke(ActionData(cell: cell as? C, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex)) + return action.invoke(ActionData(cell: cell as? C, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex, userInfo: userInfo)) } return nil } - - public func registerCell(inTableView tableView: UITableView) { - + + override 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 override var estimatedRowHeight: Float { + return C.estimatedHeight() } - - public init(items: [I]? = nil, estimatedRowHeight: CGFloat = 48) { - super.init(items: items, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight) + + public init(item: I) { + super.init(item: item, id: C.reusableIdentifier()) } - - public override func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]? = nil) -> AnyObject? { - + + public init(items: [I]? = nil) { + super.init(items: items, id: C.reusableIdentifier()) + } + + override func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? { + switch actionType { case .configure: (cell as? C)?.configureWithItem(items[itemIndex]) default: break } - return super.invokeAction(actionType, cell: cell, indexPath: indexPath, itemIndex: itemIndex) + return super.invokeAction(actionType, cell: cell, indexPath: indexPath, itemIndex: itemIndex, userInfo: userInfo) } } public extension TableRowBuilder { - + // MARK: Items manipulation public func appendItems(items: [I]) { - self.items.appendContentsOf(items) } public func clear() { - items.removeAll() } } public func +=(left: TableRowBuilder, right: I) { - left.appendItems([right]) } public func +=(left: TableRowBuilder, right: [I]) { - left.appendItems(right) } \ No newline at end of file diff --git a/Tablet/TableSectionBuilder.swift b/Tablet/TableSectionBuilder.swift index 5912ff0..57a2f3b 100644 --- a/Tablet/TableSectionBuilder.swift +++ b/Tablet/TableSectionBuilder.swift @@ -27,7 +27,7 @@ import Foundation */ public class TableSectionBuilder { - internal weak var tableView: UITableView? + weak var tableView: UITableView? private var builders = [RowBuilder]() public var headerTitle: String? @@ -38,19 +38,18 @@ public class TableSectionBuilder { 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) { + public init(headerTitle: String? = nil, footerTitle: String? = nil, rows: [RowBuilder]? = nil) { self.headerTitle = headerTitle self.footerTitle = footerTitle - if let initialRows = rowBuilders { + if let initialRows = rows { builders.appendContentsOf(initialRows) } } @@ -63,11 +62,26 @@ public class TableSectionBuilder { self.footerView = footerView self.footerHeight = footerHeight } -} -internal extension TableSectionBuilder { + // MARK: Public + + public func clear() { + builders.removeAll() + } - internal func builderAtIndex(var index: Int) -> (RowBuilder, Int)? { + public func appendRow(row: RowBuilder) { + appendRows([row]) + } + + public func appendRows(rowBuilders: [RowBuilder]) { + + if let tableView = tableView { rowBuilders.forEach { $0.registerCell(inTableView: tableView) } } + builders.appendContentsOf(rowBuilders) + } + + // MARK: Internal + + func builderAtIndex(var index: Int) -> (RowBuilder, Int)? { for builder in builders { if index < builder.numberOfRows { @@ -78,38 +92,18 @@ internal extension TableSectionBuilder { return nil } + + func willMoveToDirector(tableView: UITableView) { - internal func willMoveToDirector(tableView: UITableView) { self.tableView = tableView self.builders.forEach { $0.registerCell(inTableView: tableView) } } } -public extension TableSectionBuilder { - - public func clear() { - - builders.removeAll() - } - - public func appendRowBuilder(rowBuilder: RowBuilder) { - - appendRowBuilders([rowBuilder]) - } - - public func appendRowBuilders(rowBuilders: [RowBuilder]) { - - if let tableView = tableView { rowBuilders.forEach { $0.registerCell(inTableView: tableView) } } - builders.appendContentsOf(rowBuilders) - } -} - public func +=(left: TableSectionBuilder, right: RowBuilder) { - - left.appendRowBuilder(right) + left.appendRow(right) } public func +=(left: TableSectionBuilder, right: [RowBuilder]) { - - left.appendRowBuilders(right) + left.appendRows(right) } \ No newline at end of file diff --git a/Tablet/Tablet.h b/Tablet/Tablet.h new file mode 100644 index 0000000..c09efa2 --- /dev/null +++ b/Tablet/Tablet.h @@ -0,0 +1,24 @@ +// +// 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 Foundation; + +FOUNDATION_EXPORT double TabletVersionNumber; +FOUNDATION_EXPORT const unsigned char TabletVersionString[]; \ No newline at end of file diff --git a/Tablet/Tablet.swift b/Tablet/Tablet.swift index ce40387..0cfc919 100644 --- a/Tablet/Tablet.swift +++ b/Tablet/Tablet.swift @@ -21,7 +21,9 @@ import UIKit import Foundation -internal let kActionPerformedNotificationKey = "_action" +struct TabletNotifications { + static let CellAction = "TabletNotificationsCellAction" +} /** The actions that Tablet provides. @@ -55,13 +57,15 @@ public class ActionData { public let item: I public let itemIndex: Int public let indexPath: NSIndexPath - - init(cell: C?, indexPath: NSIndexPath, item: I, itemIndex: Int) { - + public let userInfo: [NSObject: AnyObject]? + + init(cell: C?, indexPath: NSIndexPath, item: I, itemIndex: Int, userInfo: [NSObject: AnyObject]?) { + self.cell = cell self.indexPath = indexPath self.item = item self.itemIndex = itemIndex + self.userInfo = userInfo } } @@ -88,8 +92,7 @@ public class Action { } public func invoke() { - - NSNotificationCenter.defaultCenter().postNotificationName(kActionPerformedNotificationKey, object: self) + NSNotificationCenter.defaultCenter().postNotificationName(TabletNotifications.CellAction, object: self, userInfo: userInfo) } } @@ -102,27 +105,13 @@ public protocol ConfigurableCell { typealias Item static func reusableIdentifier() -> String + static func estimatedHeight() -> Float 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. -*/ -public protocol RowBuilder { - - var numberOfRows: Int { get } - var reusableIdentifier: String { get } - var estimatedRowHeight: CGFloat { get } - - 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/Tablet/Tablet.xcodeproj/project.pbxproj b/Tablet/Tablet.xcodeproj/project.pbxproj new file mode 100644 index 0000000..49ea0b1 --- /dev/null +++ b/Tablet/Tablet.xcodeproj/project.pbxproj @@ -0,0 +1,423 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + DAC2D6741C9D743D009E9C19 /* Tablet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC2D6691C9D743D009E9C19 /* Tablet.framework */; }; + DAC2D6871C9D7517009E9C19 /* Tablet.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2D6851C9D7517009E9C19 /* Tablet.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DAC2D6901C9D799E009E9C19 /* TableDirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */; }; + DAC2D6911C9D799E009E9C19 /* TableRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */; }; + DAC2D6921C9D799E009E9C19 /* TableSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */; }; + DAC2D6931C9D799E009E9C19 /* Tablet.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68F1C9D799E009E9C19 /* Tablet.swift */; }; + DAC2D6991C9D7A3F009E9C19 /* TabletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D6961C9D7A3B009E9C19 /* TabletTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + DAC2D6751C9D743D009E9C19 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DAC2D6601C9D743D009E9C19 /* Project object */; + proxyType = 1; + remoteGlobalIDString = DAC2D6681C9D743D009E9C19; + remoteInfo = Tablet; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + DAC2D6691C9D743D009E9C19 /* Tablet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Tablet.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DAC2D6731C9D743D009E9C19 /* TabletTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TabletTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + DAC2D6841C9D7517009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DAC2D6851C9D7517009E9C19 /* Tablet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tablet.h; sourceTree = ""; }; + DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = ""; }; + DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowBuilder.swift; sourceTree = ""; }; + DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSectionBuilder.swift; sourceTree = ""; }; + DAC2D68F1C9D799E009E9C19 /* Tablet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tablet.swift; sourceTree = ""; }; + DAC2D6951C9D7A3B009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + DAC2D6961C9D7A3B009E9C19 /* TabletTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabletTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + DAC2D6651C9D743D009E9C19 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DAC2D6701C9D743D009E9C19 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + DAC2D6741C9D743D009E9C19 /* Tablet.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + DAC2D65F1C9D743D009E9C19 = { + isa = PBXGroup; + children = ( + DAC2D68B1C9D7990009E9C19 /* Classes */, + DAC2D6941C9D7A03009E9C19 /* Tests */, + DAC2D6831C9D74EE009E9C19 /* Supporting Files */, + DAC2D66A1C9D743D009E9C19 /* Products */, + ); + sourceTree = ""; + }; + DAC2D66A1C9D743D009E9C19 /* Products */ = { + isa = PBXGroup; + children = ( + DAC2D6691C9D743D009E9C19 /* Tablet.framework */, + DAC2D6731C9D743D009E9C19 /* TabletTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + DAC2D6831C9D74EE009E9C19 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + DAC2D6841C9D7517009E9C19 /* Info.plist */, + DAC2D6851C9D7517009E9C19 /* Tablet.h */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + DAC2D68B1C9D7990009E9C19 /* Classes */ = { + isa = PBXGroup; + children = ( + DAC2D68F1C9D799E009E9C19 /* Tablet.swift */, + DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */, + DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */, + DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */, + ); + name = Classes; + sourceTree = ""; + }; + DAC2D6941C9D7A03009E9C19 /* Tests */ = { + isa = PBXGroup; + children = ( + DAC2D6951C9D7A3B009E9C19 /* Info.plist */, + DAC2D6961C9D7A3B009E9C19 /* TabletTests.swift */, + ); + name = Tests; + path = ../Tests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + DAC2D6661C9D743D009E9C19 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DAC2D6871C9D7517009E9C19 /* Tablet.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + DAC2D6681C9D743D009E9C19 /* Tablet */ = { + isa = PBXNativeTarget; + buildConfigurationList = DAC2D67D1C9D743D009E9C19 /* Build configuration list for PBXNativeTarget "Tablet" */; + buildPhases = ( + DAC2D6641C9D743D009E9C19 /* Sources */, + DAC2D6651C9D743D009E9C19 /* Frameworks */, + DAC2D6661C9D743D009E9C19 /* Headers */, + DAC2D6671C9D743D009E9C19 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Tablet; + productName = Tablet; + productReference = DAC2D6691C9D743D009E9C19 /* Tablet.framework */; + productType = "com.apple.product-type.framework"; + }; + DAC2D6721C9D743D009E9C19 /* TabletTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = DAC2D6801C9D743D009E9C19 /* Build configuration list for PBXNativeTarget "TabletTests" */; + buildPhases = ( + DAC2D66F1C9D743D009E9C19 /* Sources */, + DAC2D6701C9D743D009E9C19 /* Frameworks */, + DAC2D6711C9D743D009E9C19 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + DAC2D6761C9D743D009E9C19 /* PBXTargetDependency */, + ); + name = TabletTests; + productName = TabletTests; + productReference = DAC2D6731C9D743D009E9C19 /* TabletTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + DAC2D6601C9D743D009E9C19 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = Tablet; + TargetAttributes = { + DAC2D6681C9D743D009E9C19 = { + CreatedOnToolsVersion = 7.2; + }; + DAC2D6721C9D743D009E9C19 = { + CreatedOnToolsVersion = 7.2; + }; + }; + }; + buildConfigurationList = DAC2D6631C9D743D009E9C19 /* Build configuration list for PBXProject "Tablet" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = DAC2D65F1C9D743D009E9C19; + productRefGroup = DAC2D66A1C9D743D009E9C19 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + DAC2D6681C9D743D009E9C19 /* Tablet */, + DAC2D6721C9D743D009E9C19 /* TabletTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + DAC2D6671C9D743D009E9C19 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DAC2D6711C9D743D009E9C19 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + DAC2D6641C9D743D009E9C19 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DAC2D6901C9D799E009E9C19 /* TableDirector.swift in Sources */, + DAC2D6921C9D799E009E9C19 /* TableSectionBuilder.swift in Sources */, + DAC2D6911C9D799E009E9C19 /* TableRowBuilder.swift in Sources */, + DAC2D6931C9D799E009E9C19 /* Tablet.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DAC2D66F1C9D743D009E9C19 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DAC2D6991C9D7A3F009E9C19 /* TabletTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + DAC2D6761C9D743D009E9C19 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = DAC2D6681C9D743D009E9C19 /* Tablet */; + targetProxy = DAC2D6751C9D743D009E9C19 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + DAC2D67B1C9D743D009E9C19 /* 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; + CURRENT_PROJECT_VERSION = 1; + 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"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DAC2D67C1C9D743D009E9C19 /* 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; + CURRENT_PROJECT_VERSION = 1; + 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; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DAC2D67E1C9D743D009E9C19 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + DAC2D67F1C9D743D009E9C19 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + DAC2D6811C9D743D009E9C19 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = ../Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet.TabletTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + DAC2D6821C9D743D009E9C19 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = ../Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet.TabletTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + DAC2D6631C9D743D009E9C19 /* Build configuration list for PBXProject "Tablet" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DAC2D67B1C9D743D009E9C19 /* Debug */, + DAC2D67C1C9D743D009E9C19 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DAC2D67D1C9D743D009E9C19 /* Build configuration list for PBXNativeTarget "Tablet" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DAC2D67E1C9D743D009E9C19 /* Debug */, + DAC2D67F1C9D743D009E9C19 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + DAC2D6801C9D743D009E9C19 /* Build configuration list for PBXNativeTarget "TabletTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DAC2D6811C9D743D009E9C19 /* Debug */, + DAC2D6821C9D743D009E9C19 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = DAC2D6601C9D743D009E9C19 /* Project object */; +} diff --git a/Tablet/Tablet.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Tablet/Tablet.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..78fd6b2 --- /dev/null +++ b/Tablet/Tablet.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Tablet/Tablet.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate b/Tablet/Tablet.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..fbea635 Binary files /dev/null and b/Tablet/Tablet.xcodeproj/project.xcworkspace/xcuserdata/maxsokolov.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Tablet/Tablet.xcodeproj/xcshareddata/xcschemes/Tablet.xcscheme b/Tablet/Tablet.xcodeproj/xcshareddata/xcschemes/Tablet.xcscheme new file mode 100644 index 0000000..b6e3630 --- /dev/null +++ b/Tablet/Tablet.xcodeproj/xcshareddata/xcschemes/Tablet.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tablet/Tablet.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Tablet/Tablet.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..fe2b454 --- /dev/null +++ b/Tablet/Tablet.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/Tablet/Tablet.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/xcschememanagement.plist b/Tablet/Tablet.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..96dcbf8 --- /dev/null +++ b/Tablet/Tablet.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,27 @@ + + + + + SchemeUserState + + Tablet.xcscheme_^#shared#^_ + + orderHint + 0 + + + SuppressBuildableAutocreation + + DAC2D6681C9D743D009E9C19 + + primary + + + DAC2D6721C9D743D009E9C19 + + primary + + + + + diff --git a/TabletDemo/TabletDemo/AppDelegate.swift b/TabletDemo/Classes/Application/AppDelegate.swift similarity index 100% rename from TabletDemo/TabletDemo/AppDelegate.swift rename to TabletDemo/Classes/Application/AppDelegate.swift diff --git a/TabletDemo/Classes/Presentation/Main/ViewControllers/MainViewController.swift b/TabletDemo/Classes/Presentation/Main/ViewControllers/MainViewController.swift new file mode 100644 index 0000000..3bb415a --- /dev/null +++ b/TabletDemo/Classes/Presentation/Main/ViewControllers/MainViewController.swift @@ -0,0 +1,33 @@ +// +// MainViewController.swift +// TabletDemo +// +// Created by Max Sokolov on 19/03/16. +// Copyright © 2016 Tablet. All rights reserved. +// + +import Foundation +import UIKit +import Tablet + +class MainViewController : UITableViewController { + + var tableDirector: TableDirector! + + override func viewDidLoad() { + super.viewDidLoad() + + tableDirector = TableDirector(tableView: tableView) + + tableDirector += TableRowBuilder(items: [1, 2, 3, 4], id: "cell") + .action(.configure) { data -> Void in + + data.cell?.accessoryType = .DisclosureIndicator + data.cell?.textLabel?.text = "\(data.item)" + } + .action(.click) { data -> Void in + + + } + } +} \ No newline at end of file diff --git a/TabletDemo/TabletDemo/Assets.xcassets/AppIcon.appiconset/Contents.json b/TabletDemo/Resources/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from TabletDemo/TabletDemo/Assets.xcassets/AppIcon.appiconset/Contents.json rename to TabletDemo/Resources/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/TabletDemo/TabletDemo/Info.plist b/TabletDemo/Resources/Info.plist similarity index 100% rename from TabletDemo/TabletDemo/Info.plist rename to TabletDemo/Resources/Info.plist diff --git a/TabletDemo/TabletDemo/Base.lproj/LaunchScreen.storyboard b/TabletDemo/Resources/Storyboards/LaunchScreen.storyboard similarity index 92% rename from TabletDemo/TabletDemo/Base.lproj/LaunchScreen.storyboard rename to TabletDemo/Resources/Storyboards/LaunchScreen.storyboard index ffacbd6..c9b7564 100644 --- a/TabletDemo/TabletDemo/Base.lproj/LaunchScreen.storyboard +++ b/TabletDemo/Resources/Storyboards/LaunchScreen.storyboard @@ -1,8 +1,8 @@ - + - + @@ -16,7 +16,6 @@ - diff --git a/TabletDemo/Resources/Storyboards/Main.storyboard b/TabletDemo/Resources/Storyboards/Main.storyboard new file mode 100644 index 0000000..2cf7895 --- /dev/null +++ b/TabletDemo/Resources/Storyboards/Main.storyboard @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TabletDemo/TabletDemo.xcodeproj/project.pbxproj b/TabletDemo/TabletDemo.xcodeproj/project.pbxproj index c043d8b..4d330c4 100644 --- a/TabletDemo/TabletDemo.xcodeproj/project.pbxproj +++ b/TabletDemo/TabletDemo.xcodeproj/project.pbxproj @@ -7,39 +7,21 @@ 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 */; }; - DAED08F11C14DE7E006C04D8 /* MyTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAED08F01C14DE7E006C04D8 /* MyTableViewCell.swift */; }; - DAF003961C14DC0C0028C3D6 /* MyNibTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAF003951C14DC0C0028C3D6 /* MyNibTableViewCell.swift */; }; - DAF003981C14DC250028C3D6 /* MyNibTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = DAF003971C14DC250028C3D6 /* MyNibTableViewCell.xib */; }; + DAC2D5CA1C9D303E009E9C19 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */; }; + DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */; }; + DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */; }; + DAC2D5D41C9D3118009E9C19 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D5D31C9D3118009E9C19 /* MainViewController.swift */; }; + DAC2D69C1C9E75E3009E9C19 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */; }; /* 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 = ""; }; - DAED08F01C14DE7E006C04D8 /* MyTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyTableViewCell.swift; sourceTree = ""; }; - DAF003951C14DC0C0028C3D6 /* MyNibTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyNibTableViewCell.swift; sourceTree = ""; }; - DAF003971C14DC250028C3D6 /* MyNibTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MyNibTableViewCell.xib; sourceTree = ""; }; + DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + DAC2D5D31C9D3118009E9C19 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; + DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + DAC2D69D1C9E78B5009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -56,8 +38,8 @@ DAB7EB1E1BEF787300D2AD5E = { isa = PBXGroup; children = ( - DAB7EB3C1BEF789500D2AD5E /* Tablet */, - DAB7EB291BEF787300D2AD5E /* TabletDemo */, + DAC2D5C61C9D2FE5009E9C19 /* Classes */, + DAC2D5CB1C9D3058009E9C19 /* Resources */, DAB7EB281BEF787300D2AD5E /* Products */, ); sourceTree = ""; @@ -70,34 +52,72 @@ name = Products; sourceTree = ""; }; - DAB7EB291BEF787300D2AD5E /* TabletDemo */ = { + DAC2D5C61C9D2FE5009E9C19 /* Classes */ = { isa = PBXGroup; children = ( - DAB7EB2A1BEF787300D2AD5E /* AppDelegate.swift */, - DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */, - DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */, - DA1BCD101BF7388C00CC0479 /* CustomTableActions.swift */, - DAF003951C14DC0C0028C3D6 /* MyNibTableViewCell.swift */, - DAF003971C14DC250028C3D6 /* MyNibTableViewCell.xib */, - DAED08F01C14DE7E006C04D8 /* MyTableViewCell.swift */, - DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */, - DAB7EB311BEF787300D2AD5E /* Assets.xcassets */, - DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */, - DAB7EB361BEF787300D2AD5E /* Info.plist */, + DAC2D5C81C9D3014009E9C19 /* Application */, + DAC2D5C71C9D3005009E9C19 /* Presentation */, ); - path = TabletDemo; + path = Classes; sourceTree = ""; }; - DAB7EB3C1BEF789500D2AD5E /* Tablet */ = { + DAC2D5C71C9D3005009E9C19 /* Presentation */ = { isa = PBXGroup; children = ( - DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */, - DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */, - 508B71851BF48E0D00272920 /* TableRowBuilder.swift */, - 508B71831BF48DD300272920 /* TableSectionBuilder.swift */, + DAC2D5D11C9D30D8009E9C19 /* Main */, ); - name = Tablet; - path = ../Tablet; + path = Presentation; + sourceTree = ""; + }; + DAC2D5C81C9D3014009E9C19 /* Application */ = { + isa = PBXGroup; + children = ( + DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */, + ); + path = Application; + sourceTree = ""; + }; + DAC2D5CB1C9D3058009E9C19 /* Resources */ = { + isa = PBXGroup; + children = ( + DAC2D69D1C9E78B5009E9C19 /* Info.plist */, + DAC2D69A1C9E75BE009E9C19 /* Assets */, + DAC2D5CC1C9D306C009E9C19 /* Storyboards */, + ); + path = Resources; + sourceTree = ""; + }; + DAC2D5CC1C9D306C009E9C19 /* Storyboards */ = { + isa = PBXGroup; + children = ( + DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */, + DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */, + ); + path = Storyboards; + sourceTree = ""; + }; + DAC2D5D11C9D30D8009E9C19 /* Main */ = { + isa = PBXGroup; + children = ( + DAC2D5D21C9D30E4009E9C19 /* ViewControllers */, + ); + path = Main; + sourceTree = ""; + }; + DAC2D5D21C9D30E4009E9C19 /* ViewControllers */ = { + isa = PBXGroup; + children = ( + DAC2D5D31C9D3118009E9C19 /* MainViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; + DAC2D69A1C9E75BE009E9C19 /* Assets */ = { + isa = PBXGroup; + children = ( + DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */, + ); + path = Assets; sourceTree = ""; }; /* End PBXGroup section */ @@ -126,6 +146,7 @@ DAB7EB1F1BEF787300D2AD5E /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 0720; LastUpgradeCheck = 0700; ORGANIZATIONNAME = Tablet; TargetAttributes = { @@ -158,10 +179,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - DAB7EB351BEF787300D2AD5E /* LaunchScreen.storyboard in Resources */, - DAF003981C14DC250028C3D6 /* MyNibTableViewCell.xib in Resources */, - DAB7EB321BEF787300D2AD5E /* Assets.xcassets in Resources */, - DAB7EB301BEF787300D2AD5E /* Main.storyboard in Resources */, + DAC2D5CF1C9D30A7009E9C19 /* Main.storyboard in Resources */, + DAC2D5D01C9D30A7009E9C19 /* LaunchScreen.storyboard in Resources */, + DAC2D69C1C9E75E3009E9C19 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -172,40 +192,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 508B71841BF48DD300272920 /* TableSectionBuilder.swift in Sources */, - DAB7EB2D1BEF787300D2AD5E /* ViewController.swift in Sources */, - DAB7EB3E1BEF78A400D2AD5E /* Tablet.swift in Sources */, - DAED08F11C14DE7E006C04D8 /* MyTableViewCell.swift in Sources */, - DAF003961C14DC0C0028C3D6 /* MyNibTableViewCell.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 */, + DAC2D5D41C9D3118009E9C19 /* MainViewController.swift in Sources */, + DAC2D5CA1C9D303E009E9C19 /* AppDelegate.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; @@ -294,10 +287,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - INFOPLIST_FILE = TabletDemo/Info.plist; + INFOPLIST_FILE = Resources/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.tablet.TabletDemo; + PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; }; @@ -309,10 +302,10 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - INFOPLIST_FILE = TabletDemo/Info.plist; + INFOPLIST_FILE = Resources/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.0; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.tablet.TabletDemo; + PRODUCT_BUNDLE_IDENTIFIER = com.tablet.tablet; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; }; 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 da29aef..6fe6130 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.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/TabletDemo.xcscheme b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/TabletDemo.xcscheme index 6db4123..5b59ce8 100644 --- a/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/TabletDemo.xcscheme +++ b/TabletDemo/TabletDemo.xcodeproj/xcuserdata/maxsokolov.xcuserdatad/xcschemes/TabletDemo.xcscheme @@ -28,6 +28,16 @@ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES"> + + + + primary + DAC2D5DB1C9D6433009E9C19 + + primary + + diff --git a/TabletDemo/TabletDemo/Base.lproj/Main.storyboard b/TabletDemo/TabletDemo/Base.lproj/Main.storyboard deleted file mode 100644 index 65ef436..0000000 --- a/TabletDemo/TabletDemo/Base.lproj/Main.storyboard +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TabletDemo/TabletDemo/ConfigurableTableViewCell.swift b/TabletDemo/TabletDemo/ConfigurableTableViewCell.swift deleted file mode 100644 index 05da583..0000000 --- a/TabletDemo/TabletDemo/ConfigurableTableViewCell.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// 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 "ConfigurableTableViewCell" - } - - func configureWithItem(item: Item) { - - button.setTitle("Button \(item)", forState: .Normal) - } - - @IBAction func buttonClicked(sender: UIButton) { - - Action(key: kConfigurableTableViewCellButtonClickedAction, sender: self).invoke() - } -} \ No newline at end of file diff --git a/TabletDemo/TabletDemo/CustomTableActions.swift b/TabletDemo/TabletDemo/CustomTableActions.swift deleted file mode 100644 index 5466099..0000000 --- a/TabletDemo/TabletDemo/CustomTableActions.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// 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) { - - invokeAction(.custom(kTableDirectorDidEndDisplayingCell), cell: cell, indexPath: indexPath) - } -} \ No newline at end of file diff --git a/TabletDemo/TabletDemo/MyNibTableViewCell.swift b/TabletDemo/TabletDemo/MyNibTableViewCell.swift deleted file mode 100644 index 4888fb9..0000000 --- a/TabletDemo/TabletDemo/MyNibTableViewCell.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// MyNibTableViewCell.swift -// TabletDemo -// -// Created by Max Sokolov on 07/12/15. -// Copyright © 2015 Tablet. All rights reserved. -// - -import Foundation -import UIKit - -class MyNibTableViewCell : UITableViewCell { - - -} \ No newline at end of file diff --git a/TabletDemo/TabletDemo/MyNibTableViewCell.xib b/TabletDemo/TabletDemo/MyNibTableViewCell.xib deleted file mode 100644 index 02401db..0000000 --- a/TabletDemo/TabletDemo/MyNibTableViewCell.xib +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TabletDemo/TabletDemo/MyTableViewCell.swift b/TabletDemo/TabletDemo/MyTableViewCell.swift deleted file mode 100644 index 9f03cea..0000000 --- a/TabletDemo/TabletDemo/MyTableViewCell.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// MyTableViewCell.swift -// TabletDemo -// -// Created by Max Sokolov on 07/12/15. -// Copyright © 2015 Tablet. All rights reserved. -// - -import Foundation -import UIKit - -class MyTableViewCell : UITableViewCell { - - override func layoutSubviews() { - super.layoutSubviews() - - backgroundColor = UIColor.redColor() - } -} \ No newline at end of file diff --git a/TabletDemo/TabletDemo/ViewController.swift b/TabletDemo/TabletDemo/ViewController.swift deleted file mode 100644 index 50d971f..0000000 --- a/TabletDemo/TabletDemo/ViewController.swift +++ /dev/null @@ -1,70 +0,0 @@ -// -// ViewController.swift -// TabletDemo -// -// Created by Max Sokolov on 08/11/15. -// Copyright © 2015 Tablet. All rights reserved. -// - -import UIKit - -class ViewController: UIViewController, UIScrollViewDelegate { - - @IBOutlet weak var tableView: UITableView! - var tableDirector: TableDirector! - - override func viewDidLoad() { - super.viewDidLoad() - - tableDirector = TableDirector(tableView: tableView) - tableDirector.scrollDelegate = self - - let rowBuilder = TableRowBuilder(items: [1, 2, 3, 4], id: "cell", estimatedRowHeight: 44) - .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 myRowBuilder = TableRowBuilder(item: 0, id: "cellll", estimatedRowHeight: 44) - - let sectionBuilder = TableSectionBuilder(headerTitle: "Tablet", footerTitle: "Deal with table view like a boss.", rowBuilders: [rowBuilder, configurableRowBuilder, myRowBuilder]) - - tableDirector += sectionBuilder - - sectionBuilder.appendRowBuilder(TableRowBuilder(item: 0, estimatedRowHeight: 44)) - } - - func scrollViewWillBeginDragging(scrollView: UIScrollView) { - print("begin dragging") - } -} \ No newline at end of file diff --git a/Tests/Info.plist b/Tests/Info.plist new file mode 100644 index 0000000..ba72822 --- /dev/null +++ b/Tests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/Tests/TabletTests.swift b/Tests/TabletTests.swift new file mode 100644 index 0000000..4d5c7b9 --- /dev/null +++ b/Tests/TabletTests.swift @@ -0,0 +1,200 @@ +// +// 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 XCTest +import Tablet + +class TestController: UITableViewController { + + var tableDirector: TableDirector! + + override func viewDidLoad() { + super.viewDidLoad() + tableDirector = TableDirector(tableView: tableView) + } +} + +struct TestData { + + let title: String +} + +struct TestTableViewCellOptions { + + static let ReusableIdentifier: String = "ReusableIdentifier" + static let CellAction: String = "CellAction" + static let CellActionUserInfoKey: String = "CellActionUserInfoKey" + static let CellActionUserInfoValue: String = "CellActionUserInfoValue" + static let EstimatedHeight: Float = 255 +} + +class TestTableViewCell: UITableViewCell, ConfigurableCell { + + typealias Item = TestData + + static func reusableIdentifier() -> String { + return TestTableViewCellOptions.ReusableIdentifier + } + + static func estimatedHeight() -> Float { + return TestTableViewCellOptions.EstimatedHeight + } + + func configureWithItem(item: Item) { + textLabel?.text = item.title + } + + func raiseAction() { + Action(key: TestTableViewCellOptions.CellAction, sender: self, userInfo: [TestTableViewCellOptions.CellActionUserInfoKey: TestTableViewCellOptions.CellActionUserInfoValue]).invoke() + } +} + +class TabletTests: XCTestCase { + + var testController: TestController! + + override func setUp() { + super.setUp() + + testController = TestController() + } + + override func tearDown() { + + testController = nil + super.tearDown() + } + + func testTableDirectorHasTableView() { + + XCTAssertNotNil(testController.tableView, "TestController should have table view") + XCTAssertNotNil(testController.tableDirector, "TestController should have table director") + XCTAssertNotNil(testController.tableDirector.tableView, "TableDirector should have table view") + } + + func testSimpleRowBuilderCreatesRowsAndSection() { + + let source = ["1", "2", "3"] + + let rows = TableRowBuilder(items: source) + .action(.configure) { data -> Void in + + XCTAssertNotNil(data.cell, "Action should have a cell") + data.cell?.textLabel?.text = "\(data.item)" + } + + testController.view.hidden = false + testController.tableDirector += rows + testController.tableView.reloadData() + + XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section") + XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == source.count, "Table view should have certain number of rows in a section") + + for (index, element) in source.enumerate() { + + let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0)) + + XCTAssertNotNil(cell) + XCTAssertTrue(cell?.textLabel?.text == element) + } + } + + func testConfigurableRowBuilderCreatesRowsAndSection() { + + let testData = TestData(title: "title") + + testController.view.hidden = false + testController.tableDirector += TableConfigurableRowBuilder(item: testData) + testController.tableView.reloadData() + + let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) as? TestTableViewCell + + XCTAssertNotNil(cell, "Cell should exists and should be TestTableViewCell") + XCTAssertTrue(cell?.textLabel?.text == testData.title, "Cell's textLabel.text should equal to testData's title") + } + + func testSectionBuilderCreatesSectionWithHeaderAndFooterTitles() { + + let row = TableConfigurableRowBuilder(items: [TestData(title: "title")]) + + let sectionHeaderTitle = "Header Title" + let sectionFooterTitle = "Footer Title" + + let section = TableSectionBuilder(headerTitle: sectionHeaderTitle, footerTitle: sectionFooterTitle, rows: [row]) + + testController.view.hidden = false + testController.tableDirector += section + testController.tableView.reloadData() + + XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section") + XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section") + + XCTAssertTrue(testController.tableView.dataSource?.tableView?(testController.tableView, titleForHeaderInSection: 0) == sectionHeaderTitle) + XCTAssertTrue(testController.tableView.dataSource?.tableView?(testController.tableView, titleForFooterInSection: 0) == sectionFooterTitle) + } + + func testSectionBuilderCreatesSectionWithHeaderAndFooterViews() { + + let row = TableConfigurableRowBuilder(items: [TestData(title: "title")]) + + let sectionHeaderView = UIView() + let sectionFooterView = UIView() + + let section = TableSectionBuilder(headerView: sectionHeaderView, headerHeight: 44, footerView: sectionFooterView, footerHeight: 44) + section += row + + testController.view.hidden = false + testController.tableDirector += section + testController.tableView.reloadData() + + XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section") + XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section") + + XCTAssertTrue(testController.tableView.delegate?.tableView?(testController.tableView, viewForHeaderInSection: 0) == sectionHeaderView) + XCTAssertTrue(testController.tableView.delegate?.tableView?(testController.tableView, viewForFooterInSection: 0) == sectionFooterView) + } + + func testRowBuilderCustomActionInvokedAndSentUserInfo() { + + let expectation = expectationWithDescription("cell action") + + let row = TableConfigurableRowBuilder(items: [TestData(title: "title")]) + .action(TestTableViewCellOptions.CellAction) { data -> Void in + + XCTAssertNotNil(data.cell, "Action data should have a cell") + XCTAssertNotNil(data.userInfo, "Action data should have a user info dictionary") + XCTAssertTrue(data.userInfo?[TestTableViewCellOptions.CellActionUserInfoKey] as? String == TestTableViewCellOptions.CellActionUserInfoValue, "UserInfo should have correct value for key") + + expectation.fulfill() + } + + testController.view.hidden = false + testController.tableDirector += row + testController.tableView.reloadData() + + let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) as? TestTableViewCell + + XCTAssertNotNil(cell, "Cell should exists and should be TestTableViewCell") + + cell?.raiseAction() + + waitForExpectationsWithTimeout(1.0, handler: nil) + } +} \ No newline at end of file