Merge pull request #8 from maxsokolov/develop

travis-ci
This commit is contained in:
Max Sokolov 2016-03-21 00:08:29 +04:00
commit a28422c1af
36 changed files with 1202 additions and 558 deletions

11
.travis.yml Normal file
View File

@ -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

View File

@ -1,19 +1,32 @@
![Tablet](https://raw.githubusercontent.com/maxsokolov/tablet/assets/tablet.png)
#Tablet
<p align="left">
<a href="https://travis-ci.org/maxsokolov/tablet"><img src="https://travis-ci.org/maxsokolov/tablet.svg" alt="Build Status" /></a>
<a href="https://developer.apple.com/swift"><img src="https://img.shields.io/badge/Swift2-compatible-4BC51D.svg?style=flat" alt="Swift 2 compatible" /></a>
<img src="https://img.shields.io/badge/platform-iOS-blue.svg?style=flat" alt="Platform iOS" />
<a href="https://cocoapods.org/pods/tablet"><img src="https://img.shields.io/badge/pod-0.2.4-blue.svg" alt="CocoaPods compatible" /></a>
<a href="https://cocoapods.org/pods/tablet"><img src="https://img.shields.io/badge/pod-0.3.0-blue.svg" alt="CocoaPods compatible" /></a>
<a href="https://raw.githubusercontent.com/maxsokolov/tablet/master/LICENSE"><img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License: MIT" /></a>
</p>
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<String, MyTableViewCell>(items: ["1", "2", "3", "4", "5"], estimatedRowHeight: 42)
TableConfigurableRowBuilder<String, MyTableViewCell>(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<User, UITableViewCell>(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<User, MyTableViewCell>(estimatedRowHeight: 42)
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>()
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<User, MyTableViewCell>(items: users, id: "reusable_id", estimatedRowHeight: 42)
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(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<User, MyTableViewCell>(items: users, estimatedRowHeight: 42)
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users)
.action(kTableDirectorDidEndDisplayingCell) { data -> Void in
}

View File

@ -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.'

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Tablet/Tablet.xcodeproj">
</FileRef>
<FileRef
location = "group:TabletDemo/TabletDemo.xcodeproj">
</FileRef>
</Workspace>

26
Tablet/Info.plist Normal file
View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -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)
}

View File

@ -23,13 +23,13 @@ import Foundation
public typealias ReturnValue = AnyObject?
internal enum ActionHandler<I, C> {
enum ActionHandler<I, C> {
case actionBlock((data: ActionData<I, C>) -> Void)
case actionReturnBlock((data: ActionData<I, C>) -> AnyObject?)
func invoke(data: ActionData<I, C>) -> ReturnValue {
switch (self) {
case .actionBlock(let closure):
closure(data: data)
@ -40,33 +40,50 @@ internal enum ActionHandler<I, C> {
}
}
/**
Responsible for building cells of given type and passing items to them.
*/
public class TableRowBuilder<I, C where C: UITableViewCell> : RowBuilder {
private var actions = Dictionary<String, ActionHandler<I, C>>()
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<I, C where C: UITableViewCell> : RowBuilder {
private var actions = Dictionary<String, ActionHandler<I, C>>()
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<I, C where C: UITableViewCell> : RowBuilder {
}
// MARK: Chaining actions
public func action(key: String, closure: (data: ActionData<I, C>) -> Void) -> Self {
actions[key] = .actionBlock(closure)
return self
}
public func action(actionType: ActionType, closure: (data: ActionData<I, C>) -> Void) -> Self {
actions[actionType.key] = .actionBlock(closure)
return self
}
public func action(actionType: ActionType, closure: (data: ActionData<I, C>) -> ReturnValue) -> Self {
actions[actionType.key] = .actionReturnBlock(closure)
return self
}
// 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<I, C: ConfigurableCell where C.Item == I, C: UITableViewCell> : TableRowBuilder<I, C> {
public init(item: I, estimatedRowHeight: CGFloat = 48) {
super.init(item: item, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight)
public 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 +=<I, C>(left: TableRowBuilder<I, C>, right: I) {
left.appendItems([right])
}
public func +=<I, C>(left: TableRowBuilder<I, C>, right: [I]) {
left.appendItems(right)
}

View File

@ -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)
}

24
Tablet/Tablet.h Normal file
View File

@ -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[];

View File

@ -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<I, C> {
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?
}

View File

@ -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 = "<group>"; };
DAC2D6851C9D7517009E9C19 /* Tablet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tablet.h; sourceTree = "<group>"; };
DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = "<group>"; };
DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowBuilder.swift; sourceTree = "<group>"; };
DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSectionBuilder.swift; sourceTree = "<group>"; };
DAC2D68F1C9D799E009E9C19 /* Tablet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tablet.swift; sourceTree = "<group>"; };
DAC2D6951C9D7A3B009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DAC2D6961C9D7A3B009E9C19 /* TabletTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabletTests.swift; sourceTree = "<group>"; };
/* 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 = "<group>";
};
DAC2D66A1C9D743D009E9C19 /* Products */ = {
isa = PBXGroup;
children = (
DAC2D6691C9D743D009E9C19 /* Tablet.framework */,
DAC2D6731C9D743D009E9C19 /* TabletTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
DAC2D6831C9D74EE009E9C19 /* Supporting Files */ = {
isa = PBXGroup;
children = (
DAC2D6841C9D7517009E9C19 /* Info.plist */,
DAC2D6851C9D7517009E9C19 /* Tablet.h */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
DAC2D68B1C9D7990009E9C19 /* Classes */ = {
isa = PBXGroup;
children = (
DAC2D68F1C9D799E009E9C19 /* Tablet.swift */,
DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */,
DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */,
DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */,
);
name = Classes;
sourceTree = "<group>";
};
DAC2D6941C9D7A03009E9C19 /* Tests */ = {
isa = PBXGroup;
children = (
DAC2D6951C9D7A3B009E9C19 /* Info.plist */,
DAC2D6961C9D7A3B009E9C19 /* TabletTests.swift */,
);
name = Tests;
path = ../Tests;
sourceTree = "<group>";
};
/* 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 */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:Tablet.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6681C9D743D009E9C19"
BuildableName = "Tablet.framework"
BlueprintName = "Tablet"
ReferencedContainer = "container:Tablet.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6721C9D743D009E9C19"
BuildableName = "TabletTests.xctest"
BlueprintName = "TabletTests"
ReferencedContainer = "container:Tablet.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6681C9D743D009E9C19"
BuildableName = "Tablet.framework"
BlueprintName = "Tablet"
ReferencedContainer = "container:Tablet.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6681C9D743D009E9C19"
BuildableName = "Tablet.framework"
BlueprintName = "Tablet"
ReferencedContainer = "container:Tablet.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D6681C9D743D009E9C19"
BuildableName = "Tablet.framework"
BlueprintName = "Tablet"
ReferencedContainer = "container:Tablet.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
type = "1"
version = "2.0">
</Bucket>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Tablet.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>DAC2D6681C9D743D009E9C19</key>
<dict>
<key>primary</key>
<true/>
</dict>
<key>DAC2D6721C9D743D009E9C19</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -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<Int, UITableViewCell>(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
}
}
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="8191" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--View Controller-->
@ -16,7 +16,6 @@
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="dOU-ON-YYD">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
</dependencies>
<scenes>
<!--Main View Controller-->
<scene sceneID="oP2-EZ-N5W">
<objects>
<tableViewController id="ZyD-Ww-nfe" customClass="MainViewController" customModule="TabletDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" id="xii-6e-J4f">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="cell" id="eQJ-7O-k03">
<rect key="frame" x="0.0" y="92" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="eQJ-7O-k03" id="Ufi-z4-pLt">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<connections>
<outlet property="dataSource" destination="ZyD-Ww-nfe" id="dup-HU-FfF"/>
<outlet property="delegate" destination="ZyD-Ww-nfe" id="bRX-uu-KTX"/>
</connections>
</tableView>
<navigationItem key="navigationItem" id="6ba-xC-prc"/>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="uxV-eY-exN" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1074" y="287"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="Nb3-Yi-ik8">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="dOU-ON-YYD" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="Ngx-L8-cbs">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="ZyD-Ww-nfe" kind="relationship" relationship="rootViewController" id="vRk-x4-ylh"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="1px-T5-UXL" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="334" y="287"/>
</scene>
</scenes>
</document>

View File

@ -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 = "<group>"; };
508B71851BF48E0D00272920 /* TableRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowBuilder.swift; sourceTree = "<group>"; };
DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = "<group>"; };
DA1BCD101BF7388C00CC0479 /* CustomTableActions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomTableActions.swift; sourceTree = "<group>"; };
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 = "<group>"; };
DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
DAB7EB2F1BEF787300D2AD5E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
DAB7EB311BEF787300D2AD5E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
DAB7EB341BEF787300D2AD5E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
DAB7EB361BEF787300D2AD5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tablet.swift; sourceTree = "<group>"; };
DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurableTableViewCell.swift; sourceTree = "<group>"; };
DAED08F01C14DE7E006C04D8 /* MyTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyTableViewCell.swift; sourceTree = "<group>"; };
DAF003951C14DC0C0028C3D6 /* MyNibTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyNibTableViewCell.swift; sourceTree = "<group>"; };
DAF003971C14DC250028C3D6 /* MyNibTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MyNibTableViewCell.xib; sourceTree = "<group>"; };
DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
DAC2D5D31C9D3118009E9C19 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = "<group>"; };
DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
DAC2D69D1C9E78B5009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* 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 = "<group>";
@ -70,34 +52,72 @@
name = Products;
sourceTree = "<group>";
};
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 = "<group>";
};
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 = "<group>";
};
DAC2D5C81C9D3014009E9C19 /* Application */ = {
isa = PBXGroup;
children = (
DAC2D5C91C9D303E009E9C19 /* AppDelegate.swift */,
);
path = Application;
sourceTree = "<group>";
};
DAC2D5CB1C9D3058009E9C19 /* Resources */ = {
isa = PBXGroup;
children = (
DAC2D69D1C9E78B5009E9C19 /* Info.plist */,
DAC2D69A1C9E75BE009E9C19 /* Assets */,
DAC2D5CC1C9D306C009E9C19 /* Storyboards */,
);
path = Resources;
sourceTree = "<group>";
};
DAC2D5CC1C9D306C009E9C19 /* Storyboards */ = {
isa = PBXGroup;
children = (
DAC2D5CD1C9D30A7009E9C19 /* Main.storyboard */,
DAC2D5CE1C9D30A7009E9C19 /* LaunchScreen.storyboard */,
);
path = Storyboards;
sourceTree = "<group>";
};
DAC2D5D11C9D30D8009E9C19 /* Main */ = {
isa = PBXGroup;
children = (
DAC2D5D21C9D30E4009E9C19 /* ViewControllers */,
);
path = Main;
sourceTree = "<group>";
};
DAC2D5D21C9D30E4009E9C19 /* ViewControllers */ = {
isa = PBXGroup;
children = (
DAC2D5D31C9D3118009E9C19 /* MainViewController.swift */,
);
path = ViewControllers;
sourceTree = "<group>";
};
DAC2D69A1C9E75BE009E9C19 /* Assets */ = {
isa = PBXGroup;
children = (
DAC2D69B1C9E75E3009E9C19 /* Assets.xcassets */,
);
path = Assets;
sourceTree = "<group>";
};
/* 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 = "<group>";
};
DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
DAB7EB341BEF787300D2AD5E /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* 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 = "";
};

View File

@ -28,6 +28,16 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAC2D5DB1C9D6433009E9C19"
BuildableName = "TabletTests.xctest"
BlueprintName = "TabletTests"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference

View File

@ -17,6 +17,11 @@
<key>primary</key>
<true/>
</dict>
<key>DAC2D5DB1C9D6433009E9C19</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -1,130 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="jN2-c3-xf5">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="TabletDemo" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" translatesAutoresizingMaskIntoConstraints="NO" id="ovi-uI-31e">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<animations/>
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="cell" textLabel="Rrx-qY-GXL" style="IBUITableViewCellStyleDefault" id="MhM-yS-XTS">
<rect key="frame" x="0.0" y="114" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="MhM-yS-XTS" id="1uR-sU-VS9">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Rrx-qY-GXL">
<rect key="frame" x="15" y="0.0" width="570" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<animations/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<animations/>
</tableViewCellContentView>
<animations/>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ConfigurableTableViewCell" id="GPe-Vu-z5d" customClass="ConfigurableTableViewCell" customModule="TabletDemo" customModuleProvider="target">
<rect key="frame" x="0.0" y="158" width="600" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="GPe-Vu-z5d" id="GMV-qL-pTv">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="C3u-xz-sbM">
<rect key="frame" x="14" y="5" width="70" height="34"/>
<animations/>
<constraints>
<constraint firstAttribute="width" constant="70" id="MSu-D8-6BO"/>
</constraints>
<state key="normal" title="Button">
<color key="titleColor" red="0.090196078430000007" green="0.3411764706" blue="1" alpha="1" colorSpace="calibratedRGB"/>
</state>
<connections>
<action selector="buttonClicked:" destination="GPe-Vu-z5d" eventType="touchUpInside" id="IbC-qw-alO"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="wWp-hU-I3e">
<rect key="frame" x="92" y="12" width="498" height="21"/>
<animations/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<animations/>
<constraints>
<constraint firstItem="C3u-xz-sbM" firstAttribute="leading" secondItem="GMV-qL-pTv" secondAttribute="leading" constant="14" id="6j5-PX-P9O"/>
<constraint firstItem="C3u-xz-sbM" firstAttribute="centerY" secondItem="GMV-qL-pTv" secondAttribute="centerY" id="OU7-rr-jUL"/>
<constraint firstItem="wWp-hU-I3e" firstAttribute="top" secondItem="GMV-qL-pTv" secondAttribute="top" constant="12" id="azl-YD-xyA"/>
<constraint firstItem="wWp-hU-I3e" firstAttribute="leading" secondItem="C3u-xz-sbM" secondAttribute="trailing" constant="8" id="tDZ-be-hlD"/>
<constraint firstAttribute="bottom" secondItem="wWp-hU-I3e" secondAttribute="bottom" constant="10" id="w4b-rY-Vf2"/>
<constraint firstAttribute="trailing" secondItem="wWp-hU-I3e" secondAttribute="trailing" constant="10" id="wlJ-Ga-0f7"/>
</constraints>
</tableViewCellContentView>
<animations/>
<connections>
<outlet property="button" destination="C3u-xz-sbM" id="7KC-FU-lBA"/>
<outlet property="contentLabel" destination="wWp-hU-I3e" id="uo4-ez-ea8"/>
</connections>
</tableViewCell>
</prototypes>
</tableView>
</subviews>
<animations/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="ovi-uI-31e" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="J1O-uy-ywm"/>
<constraint firstItem="ovi-uI-31e" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="N0t-Cn-F6e"/>
<constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="ovi-uI-31e" secondAttribute="bottom" id="hgJ-9n-Rwk"/>
<constraint firstAttribute="trailing" secondItem="ovi-uI-31e" secondAttribute="trailing" id="wbG-7U-YPc"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="8uO-wD-KyX"/>
<connections>
<outlet property="tableView" destination="ovi-uI-31e" id="zpd-iM-qe3"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1185" y="-317"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="2aE-PW-F7u">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="jN2-c3-xf5" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" id="4L0-SI-HVz">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="N0v-UF-dbb"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="jlp-Hv-yz1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="444" y="-317"/>
</scene>
</scenes>
</document>

View File

@ -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()
}
}

View File

@ -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)
}
}

View File

@ -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 {
}

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="9060" systemVersion="15B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9051"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="MyNibTableViewCell" id="ien-1l-Zfq" customClass="MyNibTableViewCell" customModule="TabletDemo" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ien-1l-Zfq" id="Ddg-Z7-Dq5">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<autoresizingMask key="autoresizingMask"/>
<animations/>
</tableViewCellContentView>
<animations/>
<color key="backgroundColor" red="1" green="0.54996175169999995" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<point key="canvasLocation" x="521" y="285"/>
</tableViewCell>
</objects>
</document>

View File

@ -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()
}
}

View File

@ -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<Int, UITableViewCell>(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<String, ConfigurableTableViewCell>(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<Int, MyTableViewCell>(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<Int, MyNibTableViewCell>(item: 0, estimatedRowHeight: 44))
}
func scrollViewWillBeginDragging(scrollView: UIScrollView) {
print("begin dragging")
}
}

24
Tests/Info.plist Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>

200
Tests/TabletTests.swift Normal file
View File

@ -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<String, UITableViewCell>(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<TestData, TestTableViewCell>(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<TestData, TestTableViewCell>(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<TestData, TestTableViewCell>(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<TestData, TestTableViewCell>(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)
}
}