Merge pull request #13 from maxsokolov/develop

api improvements
This commit is contained in:
Max Sokolov 2016-05-15 20:00:18 +04:00
commit 874f767528
16 changed files with 473 additions and 336 deletions

39
.gitignore vendored Executable file
View File

@ -0,0 +1,39 @@
# Mac OS X
.DS_Store
# Xcode
## Build generated
build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
.build/
# Carthage
Carthage/Build

View File

@ -1,10 +1,12 @@
#Tablet
![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/maxsokolov/tablet/assets/logo.png)
#Tablet.swift
<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>
<a href="https://developer.apple.com/swift"><img src="https://img.shields.io/badge/Swift_2.2-compatible-4BC51D.svg?style=flat" alt="Swift 2.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.4.0-blue.svg" alt="CocoaPods compatible" /></a>
<a href="https://cocoapods.org/pods/tablet"><img src="https://img.shields.io/badge/pod-0.5.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>
@ -15,18 +17,18 @@ Tablet is a super lightweight yet powerful generic library that handles a comple
- [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] Chainable cell actions (select/deselect etc.)
- [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 😘:
That's almost all you need to build a bunch of cells in a section:
```swift
TableConfigurableRowBuilder<String, MyTableViewCell>(items: ["1", "2", "3", "4", "5"])
let builder = TableRowBuilder<String, MyTableViewCell>(items: ["1", "2", "3", "4", "5"])
```
Tablet respects cells reusability feature and built with performace in mind. See the Usage section to learn more.
Tablet relies on <a href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/WorkingwithSelf-SizingTableViewCells.html" target="_blank">self-sizing table view cells</a>, respects cells reusability feature and also built with performace in mind. You don't have to worry about anything, just create your cells, setup autolayout constraints and be happy. See the Usage section to learn more.
## Requirements
@ -55,30 +57,10 @@ $ pod install
## Usage
### Very basic
You may want to setup a very basic table view, without any custom cells. In that case simply use the `TableRowBuilder`.
```swift
import Tablet
let rowBuilder = TableRowBuilder<User, UITableViewCell>(items: [user1, user2, user3], id: "reusable_id")
.action(.configure) { data -> Void in
data.cell?.textLabel?.text = data.item.username
data.cell?.detailTextLabel?.text = data.item.isActive ? "Active" : "Inactive"
}
let sectionBuilder = TableSectionBuilder(headerTitle: "Users", rows: [rowBuilder])
director = TableDirector(tableView: tableView)
director.appendSections(sectionBuilder)
```
### Type-safe configurable cells
Let's say you want to put your cell configuration logic into cell itself. Say you want to pass your view model (or even model) to your cell.
You could easily do this using the `TableConfigurableRowBuilder`. Your cell should respect the `ConfigurableCell` protocol as you may see in example below:
You could easily do this using the `TableRowBuilder`. Your cell should conforms to `ConfigurableCell` protocol as you may see in example below:
```swift
import Tablet
@ -87,6 +69,7 @@ class MyTableViewCell : UITableViewCell, ConfigurableCell {
typealias T = User
// this method is not required to be implemented if your cell's id equals to class name
static func reusableIdentifier() -> String {
return "reusable_id"
}
@ -102,16 +85,36 @@ class MyTableViewCell : UITableViewCell, ConfigurableCell {
}
}
```
Once you've implemented the protocol, simply use the `TableConfigurableRowBuilder` to build cells:
Once you've implemented the protocol, simply use the `TableRowBuilder` to build cells:
```swift
import Tablet
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>()
rowBuilder.appendItems(users)
let rowBuilder = TableRowBuilder<User, MyTableViewCell>()
rowBuilder += users
director = TableDirector(tableView: tableView)
tableDirector.appendSection(TableSectionBuilder(rows: [rowBuilder]))
tableDirector += TableSectionBuilder(rows: [rowBuilder])
```
### Very basic table view
You may want to setup a very basic table view, without any custom cells. In that case simply use the `TableBaseRowBuilder`.
```swift
import Tablet
let rowBuilder = TableBaseRowBuilder<User, UITableViewCell>(items: [user1, user2, user3], id: "reusable_id")
.action(.configure) { (data) in
data.cell?.textLabel?.text = data.item.username
data.cell?.detailTextLabel?.text = data.item.isActive ? "Active" : "Inactive"
}
let sectionBuilder = TableSectionBuilder(headerTitle: "Users", footerTitle: nil, rows: [rowBuilder])
director = TableDirector(tableView: tableView)
director += sectionBuilder
```
### Cell actions
@ -122,13 +125,13 @@ Tablet provides a chaining approach to handle actions from your cells:
import Tablet
let rowBuilder = TableRowBuilder<User, MyTableViewCell>(items: [user1, user2, user3], id: "reusable_id")
.action(.configure) { data -> Void in
.action(.configure) { (data) in
}
.action(.click) { data -> Void in
.action(.click) { (data) in
}
.action(.shouldHighlight) { data -> ReturnValue in
.valueAction(.shouldHighlight) { (data) in
return false
}
@ -138,13 +141,15 @@ let rowBuilder = TableRowBuilder<User, MyTableViewCell>(items: [user1, user2, us
```swift
import Tablet
let kMyAction = "action_key"
struct MyCellActions {
static let ButtonClicked = "ButtonClicked"
}
class MyTableViewCell : UITableViewCell {
@IBAction func buttonClicked(sender: UIButton) {
Action(key: kMyAction, sender: self, userInfo: nil).invoke()
Action(key: MyCellActions.ButtonClicked, sender: self, userInfo: nil).invoke()
}
}
```
@ -152,14 +157,14 @@ And receive this actions with your row builder:
```swift
import Tablet
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users, id: "reusable_id")
.action(.click) { data -> Void in
let rowBuilder = TableRowBuilder<User, MyTableViewCell>(items: users)
.action(.click) { (data) in
}
.action(.willDisplay) { data -> Void in
.action(.willDisplay) { (data) in
}
.action(kMyAction) { data -> Void in
.action(MyCellActions.ButtonClicked) { (data) in
}
```
@ -167,24 +172,26 @@ let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users
## Extensibility
If you find that Tablet is not provide an action you need, for example you need UITableViewDelegate's `didEndDisplayingCell` method and it's not out of the box,
simply provide an extension for `TableDirector` as follow:
simply provide an extension for `TableDirector`:
```swift
import Tablet
let kTableDirectorDidEndDisplayingCell = "enddisplaycell"
struct MyTableActions {
static let DidEndDisplayingCell = "DidEndDisplayingCell"
}
extension TableDirector {
public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
invokeAction(.custom(kTableDirectorDidEndDisplayingCell), cell: cell, indexPath: indexPath)
invoke(action: .custom(MyTableActions.DidEndDisplayingCell), cell: cell, indexPath: indexPath)
}
}
```
Catch your action with row builder:
```swift
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users)
.action(kTableDirectorDidEndDisplayingCell) { data -> Void in
let rowBuilder = TableRowBuilder<User, MyTableViewCell>(items: users)
.action(MyTableActions.DidEndDisplayingCell) { (data) -> Void in
}
```

View File

@ -1,9 +1,11 @@
Pod::Spec.new do |s|
s.name = 'Tablet'
s.version = '0.4.1'
s.module_name = 'Tablet'
s.homepage = 'https://github.com/maxsokolov/tablet'
s.summary = 'Powerful type-safe tool for UITableView. Swift 2.0 is required.'
s.version = '0.5.0'
s.homepage = 'https://github.com/maxsokolov/Tablet.swift'
s.summary = 'Powerful type-safe tool for UITableView. Swift 2.2 is required.'
s.author = { 'Max Sokolov' => 'i@maxsokolov.net' }
s.license = { :type => 'MIT', :file => 'LICENSE' }
@ -11,6 +13,5 @@ Pod::Spec.new do |s|
s.ios.deployment_target = '8.0'
s.source_files = 'Tablet/*.swift'
s.module_name = 'Tablet'
s.source = { :git => 'https://github.com/maxsokolov/tablet.git', :tag => s.version }
s.source = { :git => 'https://github.com/maxsokolov/Tablet.swift.git', :tag => s.version }
end

View File

@ -26,14 +26,14 @@ import Foundation
*/
public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
public private(set) weak var tableView: UITableView!
public unowned let tableView: UITableView
public weak var scrollDelegate: UIScrollViewDelegate?
private var sections = [TableSectionBuilder]()
public init(tableView: UITableView) {
super.init()
self.tableView = tableView
super.init()
self.tableView.delegate = self
self.tableView.dataSource = self
@ -41,7 +41,6 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
@ -61,10 +60,10 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
// MARK: Public
public func invokeAction(action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath) -> AnyObject? {
public func invoke(action 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)
return builder.0.invoke(action: action, cell: cell, indexPath: indexPath, itemIndex: builder.1, userInfo: nil)
}
public override func respondsToSelector(selector: Selector) -> Bool {
@ -75,130 +74,118 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
return scrollDelegate?.respondsToSelector(selector) == true ? scrollDelegate : super.forwardingTargetForSelector(selector)
}
// MARK: Internal
// 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)
builder.0.invoke(action: .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 {
public func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].numberOfRowsInSection
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let builder = builderAtIndexPath(indexPath)
let cell = tableView.dequeueReusableCellWithIdentifier(builder.0.reusableIdentifier, forIndexPath: indexPath)
if cell.frame.size.width != tableView.frame.size.width {
cell.frame = CGRectMake(0, 0, tableView.frame.size.width, cell.frame.size.height)
cell.layoutIfNeeded()
}
builder.0.invokeAction(.configure, cell: cell, indexPath: indexPath, itemIndex: builder.1, userInfo: nil)
builder.0.invoke(action: .configure, cell: cell, indexPath: indexPath, itemIndex: builder.1, userInfo: nil)
return cell
}
}
public extension TableDirector {
// MARK: UITableViewDataSource - section setup
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].headerTitle
}
func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return sections[section].footerTitle
}
// MARK: UITableViewDelegate - section setup
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
public func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return sections[section].headerView
}
func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
public func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return sections[section].footerView
}
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return sections[section].headerHeight
public func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return sections[section].headerView?.frame.size.height ?? UITableViewAutomaticDimension
}
func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return sections[section].footerHeight
public func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return sections[section].footerView?.frame.size.height ?? UITableViewAutomaticDimension
}
}
public extension TableDirector {
// MARK: UITableViewDelegate - actions
func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
public func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return CGFloat(builderAtIndexPath(indexPath).0.estimatedRowHeight)
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return invokeAction(.height, cell: nil, indexPath: indexPath) as? CGFloat ?? UITableViewAutomaticDimension
public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return invoke(action: .height, cell: nil, indexPath: indexPath) as? CGFloat ?? UITableViewAutomaticDimension
}
/*func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
return invokeAction(.willSelect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? NSIndexPath
}*/
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
if invokeAction(.click, cell: cell, indexPath: indexPath) != nil {
if invoke(action: .click, cell: cell, indexPath: indexPath) != nil {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
} else {
invokeAction(.select, cell: cell, indexPath: indexPath)
invoke(action: .select, cell: cell, indexPath: indexPath)
}
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
invokeAction(.deselect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath)
public func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
invoke(action: .deselect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath)
}
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
invokeAction(.willDisplay, cell: cell, indexPath: indexPath)
public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
invoke(action: .willDisplay, cell: cell, indexPath: indexPath)
}
func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return invokeAction(.shouldHighlight, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? true
public func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return invoke(action: .shouldHighlight, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? true
}
}
public extension TableDirector {
// MARK: Sections manipulation
/*func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
return invokeAction(.willSelect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? NSIndexPath
}*/
// MARK: - Sections manipulation -
public func append(section section: TableSectionBuilder) {
append(sections: [section])
}
public func append(sections sections: [TableSectionBuilder]) {
sections.forEach { $0.willMoveToDirector(tableView) }
sections.forEach { $0.tableDirector = self }
self.sections.appendContentsOf(sections)
}

View File

@ -23,157 +23,113 @@ import Foundation
public typealias ReturnValue = AnyObject?
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 {
enum ActionHandler<DataType, CellType> {
case Handler((data: ActionData<DataType, CellType>) -> Void)
case ValueHandler((data: ActionData<DataType, CellType>) -> AnyObject?)
func invoke(data: ActionData<DataType, CellType>) -> ReturnValue {
switch (self) {
case .actionBlock(let closure):
closure(data: data)
return true
case .actionReturnBlock(let closure):
return closure(data: data)
case .Handler(let handler):
handler(data: data)
return nil
case .ValueHandler(let handler):
return handler(data: data)
}
}
}
public class RowBuilder : NSObject {
public protocol RowBuilder {
public private(set) var reusableIdentifier: String
public var numberOfRows: Int {
return 0
}
public var estimatedRowHeight: Float {
return 44
}
var reusableIdentifier: String { get }
var numberOfRows: Int { get }
var estimatedRowHeight: Float { get }
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) {
}
func invoke(action action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject?
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 {
public class TableBaseRowBuilder<DataType, CellType where CellType: UITableViewCell> : RowBuilder {
private var actions = Dictionary<String, ActionHandler<I, C>>()
private var items = [I]()
private var actions = [String: ActionHandler<DataType, CellType>]()
private var items = [DataType]()
public override var numberOfRows: Int {
public let reusableIdentifier: String
public var numberOfRows: Int {
return items.count
}
public init(item: I, id: String? = nil) {
super.init(id: id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? "")
public var estimatedRowHeight: Float {
return 44
}
public init(item: DataType, id: String? = nil) {
reusableIdentifier = id ?? String(CellType)
items.append(item)
}
public init(items: [I]? = nil, id: String? = nil) {
super.init(id: id ?? NSStringFromClass(C).componentsSeparatedByString(".").last ?? "")
if items != nil {
self.items.appendContentsOf(items!)
public init(items: [DataType]? = nil, id: String? = nil) {
reusableIdentifier = id ?? String(CellType)
if let items = items {
self.items.appendContentsOf(items)
}
}
// MARK: Chaining actions
// MARK: - Chaining actions -
public func action(key: String, closure: (data: ActionData<I, C>) -> Void) -> Self {
public func action(key: String, handler: (data: ActionData<DataType, CellType>) -> Void) -> Self {
actions[key] = .actionBlock(closure)
actions[key] = .Handler(handler)
return self
}
public func action(actionType: ActionType, closure: (data: ActionData<I, C>) -> Void) -> Self {
actions[actionType.key] = .actionBlock(closure)
public func action(type: ActionType, handler: (data: ActionData<DataType, CellType>) -> Void) -> Self {
actions[type.key] = .Handler(handler)
return self
}
public func action(actionType: ActionType, closure: (data: ActionData<I, C>) -> ReturnValue) -> Self {
public func valueAction(type: ActionType, handler: (data: ActionData<DataType, CellType>) -> ReturnValue) -> Self {
actions[actionType.key] = .actionReturnBlock(closure)
actions[type.key] = .ValueHandler(handler)
return self
}
// MARK: Internal
override func invokeAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
public func invoke(action action: 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, userInfo: userInfo))
if let action = actions[action.key] {
return action.invoke(ActionData(cell: cell as? CellType, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex, userInfo: userInfo))
}
return nil
}
override func registerCell(inTableView tableView: UITableView) {
public func registerCell(inTableView tableView: UITableView) {
if tableView.dequeueReusableCellWithIdentifier(reusableIdentifier) != nil {
return
}
guard let resource = NSStringFromClass(C).componentsSeparatedByString(".").last else { return }
let bundle = NSBundle(forClass: C.self)
let resource = String(CellType)
let bundle = NSBundle(forClass: CellType.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)
tableView.registerClass(CellType.self, forCellReuseIdentifier: reusableIdentifier)
}
}
}
/**
Responsible for building configurable cells of given type and passing items to them.
*/
public class TableConfigurableRowBuilder<I, C: ConfigurableCell where C.T == I, C: UITableViewCell> : TableRowBuilder<I, C> {
public override var estimatedRowHeight: Float {
return C.estimatedHeight()
}
// MARK: - Items manipulation -
public init(item: I) {
super.init(item: item, id: C.reusableIdentifier())
}
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)?.configure(items[itemIndex])
default: break
}
return super.invokeAction(actionType, cell: cell, indexPath: indexPath, itemIndex: itemIndex, userInfo: userInfo)
}
}
public extension TableRowBuilder {
// MARK: Items manipulation
public func append(items items: [I]) {
public func append(items items: [DataType]) {
self.items.appendContentsOf(items)
}
@ -182,10 +138,36 @@ public extension TableRowBuilder {
}
}
public func +=<I, C>(left: TableRowBuilder<I, C>, right: I) {
/**
Responsible for building configurable cells of given type and passing items to them.
*/
public class TableRowBuilder<DataType, CellType: ConfigurableCell where CellType.T == DataType, CellType: UITableViewCell> : TableBaseRowBuilder<DataType, CellType> {
public override var estimatedRowHeight: Float {
return CellType.estimatedHeight()
}
public init(item: DataType) {
super.init(item: item, id: CellType.reusableIdentifier())
}
public init(items: [DataType]? = nil) {
super.init(items: items, id: CellType.reusableIdentifier())
}
public override func invoke(action action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
if case .configure = action {
(cell as? CellType)?.configure(items[itemIndex])
}
return super.invoke(action: action, cell: cell, indexPath: indexPath, itemIndex: itemIndex, userInfo: userInfo)
}
}
public func +=<DataType, CellType>(left: TableBaseRowBuilder<DataType, CellType>, right: DataType) {
left.append(items: [right])
}
public func +=<I, C>(left: TableRowBuilder<I, C>, right: [I]) {
public func +=<DataType, CellType>(left: TableBaseRowBuilder<DataType, CellType>, right: [DataType]) {
left.append(items: right)
}

View File

@ -26,44 +26,49 @@ import Foundation
Can host several row builders.
*/
public class TableSectionBuilder {
weak var tableView: UITableView?
private var builders = [RowBuilder]()
weak var tableDirector: TableDirector? {
didSet {
guard let director = tableDirector else { return }
builders.forEach { $0.registerCell(inTableView: director.tableView) }
}
}
private var builders = [RowBuilder]()
public var headerTitle: String?
public var footerTitle: String?
public var headerView: UIView?
public var headerHeight: CGFloat = UITableViewAutomaticDimension
public var footerView: UIView?
public var footerHeight: CGFloat = UITableViewAutomaticDimension
public private(set) var headerView: UIView?
public private(set) var footerView: UIView?
/// 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, rows: [RowBuilder]? = nil) {
self.headerTitle = headerTitle
self.footerTitle = footerTitle
public init(rows: [RowBuilder]? = nil) {
if let initialRows = rows {
builders.appendContentsOf(initialRows)
}
}
public init(headerView: UIView? = nil, headerHeight: CGFloat = UITableViewAutomaticDimension, footerView: UIView? = nil, footerHeight: CGFloat = UITableViewAutomaticDimension) {
self.headerView = headerView
self.headerHeight = headerHeight
public convenience init(headerTitle: String?, footerTitle: String?, rows: [RowBuilder]?) {
self.init(rows: rows)
self.footerView = footerView
self.footerHeight = footerHeight
self.headerTitle = headerTitle
self.footerTitle = footerTitle
}
// MARK: Public
public convenience init(headerView: UIView?, footerView: UIView?, rows: [RowBuilder]?) {
self.init(rows: rows)
self.headerView = headerView
self.footerView = footerView
}
// MARK: - Public -
public func clear() {
builders.removeAll()
@ -75,11 +80,11 @@ public class TableSectionBuilder {
public func append(rows rows: [RowBuilder]) {
if let tableView = tableView { rows.forEach { $0.registerCell(inTableView: tableView) } }
if let tableView = tableDirector?.tableView { rows.forEach { $0.registerCell(inTableView: tableView) } }
builders.appendContentsOf(rows)
}
// MARK: Internal
// MARK: - Internal -
func builderAtIndex(index: Int) -> (RowBuilder, Int)? {
@ -93,12 +98,6 @@ public class TableSectionBuilder {
return nil
}
func willMoveToDirector(tableView: UITableView) {
self.tableView = tableView
self.builders.forEach { $0.registerCell(inTableView: tableView) }
}
}
public func +=(left: TableSectionBuilder, right: RowBuilder) {

View File

@ -51,15 +51,15 @@ public enum ActionType {
}
}
public class ActionData<I, C> {
public class ActionData<DataType, CellType> {
public let cell: C?
public let item: I
public let cell: CellType?
public let item: DataType
public let itemIndex: Int
public let indexPath: NSIndexPath
public let userInfo: [NSObject: AnyObject]?
init(cell: C?, indexPath: NSIndexPath, item: I, itemIndex: Int, userInfo: [NSObject: AnyObject]?) {
init(cell: CellType?, indexPath: NSIndexPath, item: DataType, itemIndex: Int, userInfo: [NSObject: AnyObject]?) {
self.cell = cell
self.indexPath = indexPath
@ -112,6 +112,6 @@ public protocol ConfigurableCell {
public extension ConfigurableCell where Self: UITableViewCell {
static func reusableIdentifier() -> String {
return NSStringFromClass(self).componentsSeparatedByString(".").last ?? ""
return String(self)
}
}

View File

@ -0,0 +1,33 @@
//
// HeaderFooterController.swift
// TabletDemo
//
// Created by Max Sokolov on 16/04/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import UIKit
import Tablet
class HeaderFooterController: UIViewController {
@IBOutlet weak var tableView: UITableView! {
didSet {
tableDirector = TableDirector(tableView: tableView)
}
}
var tableDirector: TableDirector!
override func viewDidLoad() {
super.viewDidLoad()
let rows = TableRowBuilder<String, StoryboardTableViewCell>(items: ["3", "4", "5"])
let headerView = UIView(frame: CGRectMake(0, 0, 100, 100))
headerView.backgroundColor = UIColor.lightGrayColor()
let section = TableSectionBuilder(headerView: headerView, footerView: nil, rows: [rows])
tableDirector += section
}
}

View File

@ -0,0 +1,31 @@
//
// MainController.swift
// TabletDemo
//
// Created by Max Sokolov on 16/04/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import UIKit
import Tablet
class MainController: UIViewController {
@IBOutlet weak var tableView: UITableView! {
didSet {
tableDirector = TableDirector(tableView: tableView)
}
}
var tableDirector: TableDirector!
override func viewDidLoad() {
super.viewDidLoad()
let rows = TableRowBuilder<String, StoryboardTableViewCell>(items: ["1", "2", "3"])
.action(.click) { [unowned self] e in
self.performSegueWithIdentifier("headerfooter", sender: nil)
}
tableDirector += rows
}
}

View File

@ -1,33 +0,0 @@
//
// 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

@ -0,0 +1,23 @@
//
// StoryboardTableViewCell.swift
// TabletDemo
//
// Created by Max Sokolov on 16/04/16.
// Copyright © 2016 Tablet. All rights reserved.
//
import UIKit
import Tablet
class StoryboardTableViewCell: UITableViewCell, ConfigurableCell {
typealias T = String
func configure(value: T) {
textLabel?.text = value
}
static func estimatedHeight() -> Float {
return 44
}
}

View File

@ -1,39 +1,10 @@
<?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">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10116" 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"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</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>
@ -45,12 +16,101 @@
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="ZyD-Ww-nfe" kind="relationship" relationship="rootViewController" id="vRk-x4-ylh"/>
<segue destination="grv-aL-Qbb" kind="relationship" relationship="rootViewController" id="0SZ-hs-iUi"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="1px-T5-UXL" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="334" y="287"/>
</scene>
<!--Main Controller-->
<scene sceneID="bgC-Xq-OSw">
<objects>
<viewController id="grv-aL-Qbb" customClass="MainController" customModule="TabletDemo" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="COn-EH-LKP"/>
<viewControllerLayoutGuide type="bottom" id="iga-ib-rj1"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="5uw-uC-8lc">
<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="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="o67-xJ-fPW">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="StoryboardTableViewCell" id="nE5-Y5-OFf" customClass="StoryboardTableViewCell" customModule="TabletDemo" customModuleProvider="target">
<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="nE5-Y5-OFf" id="3yF-sl-yNq">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
</tableView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="iga-ib-rj1" firstAttribute="top" secondItem="o67-xJ-fPW" secondAttribute="bottom" id="jrJ-xl-S4k"/>
<constraint firstItem="o67-xJ-fPW" firstAttribute="top" secondItem="5uw-uC-8lc" secondAttribute="top" id="ntC-Lp-77v"/>
<constraint firstAttribute="trailing" secondItem="o67-xJ-fPW" secondAttribute="trailing" id="s2g-E1-S4V"/>
<constraint firstItem="o67-xJ-fPW" firstAttribute="leading" secondItem="5uw-uC-8lc" secondAttribute="leading" id="uWa-n0-GSQ"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="HPV-jJ-NPc"/>
<connections>
<outlet property="tableView" destination="o67-xJ-fPW" id="A8B-MV-tNa"/>
<segue destination="sSs-TX-Ch0" kind="show" identifier="headerfooter" id="Nbk-od-yC5"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="xij-Hw-J33" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1042" y="287"/>
</scene>
<!--Header Footer Controller-->
<scene sceneID="Jd0-of-RF9">
<objects>
<viewController id="sSs-TX-Ch0" customClass="HeaderFooterController" customModule="TabletDemo" customModuleProvider="target" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="s9s-Gu-3M6"/>
<viewControllerLayoutGuide type="bottom" id="Tux-up-dnH"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="CB5-10-J2D">
<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="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="uLr-Ff-utK">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="StoryboardTableViewCell" id="lqW-2N-6mf" customClass="StoryboardTableViewCell" customModule="TabletDemo" customModuleProvider="target">
<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="lqW-2N-6mf" id="tjN-ow-437">
<rect key="frame" x="0.0" y="0.0" width="600" height="43"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
</tableView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="Tux-up-dnH" firstAttribute="top" secondItem="uLr-Ff-utK" secondAttribute="bottom" id="0jr-6S-PFB"/>
<constraint firstItem="uLr-Ff-utK" firstAttribute="top" secondItem="CB5-10-J2D" secondAttribute="top" id="KYu-01-hlH"/>
<constraint firstAttribute="trailing" secondItem="uLr-Ff-utK" secondAttribute="trailing" id="TKR-R1-lBY"/>
<constraint firstItem="uLr-Ff-utK" firstAttribute="leading" secondItem="CB5-10-J2D" secondAttribute="leading" id="aG6-R3-QUr"/>
</constraints>
</view>
<navigationItem key="navigationItem" id="ogA-Id-clb"/>
<connections>
<outlet property="tableView" destination="uLr-Ff-utK" id="olP-LV-VmT"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Gim-O8-I3d" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1763" y="287"/>
</scene>
</scenes>
</document>

View File

@ -10,8 +10,10 @@
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 */; };
DACB71761CC2D63D00432BD3 /* MainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DACB71751CC2D63D00432BD3 /* MainController.swift */; };
DACB71781CC2D6ED00432BD3 /* StoryboardTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DACB71771CC2D6ED00432BD3 /* StoryboardTableViewCell.swift */; };
DACB717A1CC2D89D00432BD3 /* HeaderFooterController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DACB71791CC2D89D00432BD3 /* HeaderFooterController.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -19,9 +21,11 @@
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>"; };
DACB71751CC2D63D00432BD3 /* MainController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainController.swift; sourceTree = "<group>"; };
DACB71771CC2D6ED00432BD3 /* StoryboardTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryboardTableViewCell.swift; sourceTree = "<group>"; };
DACB71791CC2D89D00432BD3 /* HeaderFooterController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderFooterController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -64,7 +68,8 @@
DAC2D5C71C9D3005009E9C19 /* Presentation */ = {
isa = PBXGroup;
children = (
DAC2D5D11C9D30D8009E9C19 /* Main */,
DACB71731CC2D5ED00432BD3 /* Controllers */,
DACB71741CC2D5FD00432BD3 /* Views */,
);
path = Presentation;
sourceTree = "<group>";
@ -96,22 +101,6 @@
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 = (
@ -120,6 +109,23 @@
path = Assets;
sourceTree = "<group>";
};
DACB71731CC2D5ED00432BD3 /* Controllers */ = {
isa = PBXGroup;
children = (
DACB71751CC2D63D00432BD3 /* MainController.swift */,
DACB71791CC2D89D00432BD3 /* HeaderFooterController.swift */,
);
path = Controllers;
sourceTree = "<group>";
};
DACB71741CC2D5FD00432BD3 /* Views */ = {
isa = PBXGroup;
children = (
DACB71771CC2D6ED00432BD3 /* StoryboardTableViewCell.swift */,
);
path = Views;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -192,8 +198,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DAC2D5D41C9D3118009E9C19 /* MainViewController.swift in Sources */,
DACB71781CC2D6ED00432BD3 /* StoryboardTableViewCell.swift in Sources */,
DACB71761CC2D63D00432BD3 /* MainController.swift in Sources */,
DAC2D5CA1C9D303E009E9C19 /* AppDelegate.swift in Sources */,
DACB717A1CC2D89D00432BD3 /* HeaderFooterController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -93,7 +93,7 @@ class TabletTests: XCTestCase {
let source = ["1", "2", "3"]
let rows = TableRowBuilder<String, UITableViewCell>(items: source)
let rows = TableBaseRowBuilder<String, UITableViewCell>(items: source)
.action(.configure) { data -> Void in
XCTAssertNotNil(data.cell, "Action should have a cell")
@ -121,7 +121,7 @@ class TabletTests: XCTestCase {
let testData = TestData(title: "title")
testController.view.hidden = false
testController.tableDirector += TableConfigurableRowBuilder<TestData, TestTableViewCell>(item: testData)
testController.tableDirector += TableRowBuilder<TestData, TestTableViewCell>(item: testData)
testController.tableView.reloadData()
let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) as? TestTableViewCell
@ -132,7 +132,7 @@ class TabletTests: XCTestCase {
func testSectionBuilderCreatesSectionWithHeaderAndFooterTitles() {
let row = TableConfigurableRowBuilder<TestData, TestTableViewCell>(items: [TestData(title: "title")])
let row = TableRowBuilder<TestData, TestTableViewCell>(items: [TestData(title: "title")])
let sectionHeaderTitle = "Header Title"
let sectionFooterTitle = "Footer Title"
@ -152,12 +152,12 @@ class TabletTests: XCTestCase {
func testSectionBuilderCreatesSectionWithHeaderAndFooterViews() {
let row = TableConfigurableRowBuilder<TestData, TestTableViewCell>(items: [TestData(title: "title")])
let row = TableRowBuilder<TestData, TestTableViewCell>(items: [TestData(title: "title")])
let sectionHeaderView = UIView()
let sectionFooterView = UIView()
let section = TableSectionBuilder(headerView: sectionHeaderView, headerHeight: 44, footerView: sectionFooterView, footerHeight: 44)
let section = TableSectionBuilder(headerView: sectionHeaderView, footerView: sectionFooterView, rows: nil)
section += row
testController.view.hidden = false
@ -175,7 +175,7 @@ class TabletTests: XCTestCase {
let expectation = expectationWithDescription("cell action")
let row = TableConfigurableRowBuilder<TestData, TestTableViewCell>(items: [TestData(title: "title")])
let row = TableRowBuilder<TestData, TestTableViewCell>(items: [TestData(title: "title")])
.action(TestTableViewCellOptions.CellAction) { data -> Void in
XCTAssertNotNil(data.cell, "Action data should have a cell")