Merge pull request #1 from maxsokolov/develop

v0.1.0
This commit is contained in:
Max Sokolov 2015-11-15 21:07:32 +03:00
commit 36954090ff
24 changed files with 1669 additions and 1 deletions

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

178
README.md
View File

@ -1 +1,177 @@
# Tablet
![Tablet](https://raw.githubusercontent.com/maxsokolov/tablet/assets/tablet.png)
<p align="left">
<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.1.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:
```swift
TableConfigurableRowBuilder<String, MyTableViewCell>(items: ["1", "2", "3", "4", "5"], estimatedRowHeight: 42)
```
Tablet respects cells reusability feature and it's type-safe. See the Usage section to learn more.
## Requirements
- iOS 8.0+
- Xcode 7.1+
## Installation
### CocoaPods
To integrate Tablet into your Xcode project using CocoaPods, specify it in your `Podfile`:
```ruby
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'Tablet'
```
Then, run the following command:
```bash
$ pod install
```
## Usage
### Very basic
You may want to setup a very basic table view, without any custom cells. In that case simply use the `TableRowBuilder`.
```swift
import Tablet
let rowBuilder = TableRowBuilder<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", rowBuilders: [rowBuilder])
director = TableDirector(tableView: tableView)
director.appendSections(sectionBuilder)
```
### Type-safe configurable cells
Let's say you want to put your cell configuration logic into cell itself. Say you want to pass your view model (or even model) to your cell.
You could easily do this using the `TableConfigurableRowBuilder`. Your cell should respect the `ConfigurableCell` protocol as you may see in example below:
```swift
import Tablet
class MyTableViewCell : UITableViewCell, ConfigurableCell {
typealias Item = User
static func reusableIdentifier() -> String {
return "reusable_id"
}
func configureWithItem(item: Item) { // item is user here
textLabel?.text = item.username
detailTextLabel?.text = item.isActive ? "Active" : "Inactive"
}
}
```
Once you've implemented the protocol, simply use the `TableConfigurableRowBuilder` to build cells:
```swift
import Tablet
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(estimatedRowHeight: 42)
rowBuilder.appendItems(users)
director = TableDirector(tableView: tableView)
tableDirector.appendSection(TableSectionBuilder(rowBuilders: [rowBuilder]))
```
### Cell actions
Tablet provides a chaining approach to handle actions from your cells:
```swift
import Tablet
let rowBuilder = TableRowBuilder<User, MyTableViewCell>(items: [user1, user2, user3], id: "reusable_id")
.action(.configure) { data in
}
.action(.click) { data in
}
.action(.shouldHighlight) { data in
return false
}
```
### Custom cell actions
```swift
import Tablet
let kMyAction = "action_key"
class MyTableViewCell : UITableViewCell {
@IBAction func buttonClicked(sender: UIButton) {
Action(key: kMyAction, sender: self, userInfo: nil).perform()
}
}
```
And receive this actions with your row builder:
```swift
import Tablet
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users, id: "reusable_id", estimatedRowHeight: 42)
.action(.click) { data in
}
.action(.willDisplay) { data in
}
.action(kMyAction) { data in
}
```
## Extensibility
If you find that Tablet is not provide an action you need, for example you need UITableViewDelegate's `didEndDisplayingCell` method and it's not out of the box,
simply provide an extension for `TableDirector` as follow:
```swift
import Tablet
let kTableDirectorDidEndDisplayingCell = "enddisplaycell"
extension TableDirector {
public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
performAction(.custom(kTableDirectorDidEndDisplayingCell), cell: cell, indexPath: indexPath)
}
}
```
Catch your action with row builder:
```swift
let rowBuilder = TableConfigurableRowBuilder<User, MyTableViewCell>(items: users, estimatedRowHeight: 42)
.action(kTableDirectorDidEndDisplayingCell) { data in
}
```
You could also perform an action that returns a value.
## License
Tablet is available under the MIT license. See LICENSE for details.

16
Tablet.podspec Normal file
View File

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

187
Tablet/TableDirector.swift Normal file
View File

@ -0,0 +1,187 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
import Foundation
/**
Responsible for table view's datasource and delegate.
*/
public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
private weak var tableView: UITableView!
private var sections = [TableSectionBuilder]()
public init(tableView: UITableView) {
super.init()
self.tableView = tableView
self.tableView.delegate = self
self.tableView.dataSource = self
NSNotificationCenter.defaultCenter().addObserver(self, selector: "didReceiveAction:", name: kActionPerformedNotificationKey, object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
// MARK: Sections manipulation
public func appendSection(section: TableSectionBuilder) {
sections.append(section)
}
public func appendSections(sections: [TableSectionBuilder]) {
self.sections.appendContentsOf(sections)
}
// MARK: Private methods
/**
Find a row builder that responsible for building a row from cell with given item type.
- Parameters:
- indexPath: path of cell to dequeue
- Returns: A touple - (builder, builderItemIndex)
*/
private func builderAtIndexPath(indexPath: NSIndexPath) -> (RowBuilder, Int) {
return sections[indexPath.section].builderAtIndex(indexPath.row)!
}
public func performAction(action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath) -> AnyObject? {
let builder = builderAtIndexPath(indexPath)
return builder.0.performAction(action, cell: cell, indexPath: indexPath, itemIndex: builder.1)
}
internal func didReceiveAction(notification: NSNotification) {
if let action = notification.object as? Action, indexPath = tableView.indexPathForCell(action.cell) {
let builder = builderAtIndexPath(indexPath)
builder.0.performAction(.custom(action.key), cell: action.cell, indexPath: indexPath, itemIndex: builder.1)
}
}
// MARK: UITableViewDataSource - configuration
public func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return sections.count
}
public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].numberOfRowsInSection
}
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let builder = builderAtIndexPath(indexPath)
let cell = tableView.dequeueReusableCellWithIdentifier(builder.0.reusableIdentifier, forIndexPath: indexPath)
builder.0.performAction(.configure, cell: cell, indexPath: indexPath, itemIndex: builder.1)
return cell
}
}
extension TableDirector {
// MARK: UITableViewDataSource - section setup
public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].headerTitle
}
public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return sections[section].footerTitle
}
// MARK: UITableViewDelegate - section setup
public func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return sections[section].headerView
}
public func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return sections[section].footerView
}
public func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return sections[section].headerHeight
}
public func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
return sections[section].footerHeight
}
}
extension TableDirector {
// MARK: UITableViewDelegate - actions
public func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return builderAtIndexPath(indexPath).0.estimatedRowHeight
}
public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return performAction(.height, cell: nil, indexPath: indexPath) as? CGFloat ?? UITableViewAutomaticDimension
}
public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
if performAction(.click, cell: cell, indexPath: indexPath) != nil {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
} else {
performAction(.select, cell: cell, indexPath: indexPath)
}
}
public func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
performAction(.deselect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath)
}
public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
performAction(.willDisplay, cell: cell, indexPath: indexPath)
}
public func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return performAction(.shouldHighlight, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? true
}
}

View File

@ -0,0 +1,141 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
import Foundation
public typealias ReturnValue = AnyObject?
internal enum ActionHandler<I, C> {
case actionBlock((data: ActionData<I, C>) -> Void)
case actionReturnBlock((data: ActionData<I, C>) -> AnyObject?)
func call(data: ActionData<I, C>) -> AnyObject? {
switch (self) {
case .actionBlock(let closure):
closure(data: data)
return true
case .actionReturnBlock(let closure):
return closure(data: data)
}
}
}
/**
Responsible for building cells of given type and passing items to them.
*/
public class TableRowBuilder<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 var numberOfRows: Int {
get {
return items.count
}
}
public init(item: I, id: String, estimatedRowHeight: CGFloat = UITableViewAutomaticDimension) {
reusableIdentifier = id
self.estimatedRowHeight = estimatedRowHeight
items.append(item)
}
public init(items: [I]? = nil, id: String, estimatedRowHeight: CGFloat = UITableViewAutomaticDimension) {
reusableIdentifier = id
self.estimatedRowHeight = estimatedRowHeight
if items != nil {
self.items.appendContentsOf(items!)
}
}
// MARK: Items manipulation
public func appendItems(items: [I]) {
self.items.appendContentsOf(items)
}
public func clear() {
items.removeAll()
}
// MARK: Chaining actions
public func action(key: String, closure: (data: ActionData<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
public func performAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int) -> AnyObject? {
if let action = actions[actionType.key] {
return action.call(ActionData(cell: cell as? C, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex))
}
return nil
}
}
/**
Responsible for building configurable cells of given type and passing items to them.
*/
public class TableConfigurableRowBuilder<I, C: ConfigurableCell where C.Item == I, C: UITableViewCell> : TableRowBuilder<I, C> {
public init(item: I, estimatedRowHeight: CGFloat = UITableViewAutomaticDimension) {
super.init(item: item, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight)
}
public init(items: [I]? = nil, estimatedRowHeight: CGFloat = UITableViewAutomaticDimension) {
super.init(items: items, id: C.reusableIdentifier(), estimatedRowHeight: estimatedRowHeight)
}
public override func performAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int) -> AnyObject? {
switch actionType {
case .configure:
(cell as? C)?.configureWithItem(items[itemIndex])
default: break
}
return super.performAction(actionType, cell: cell, indexPath: indexPath, itemIndex: itemIndex)
}
}

View File

@ -0,0 +1,77 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
import Foundation
/**
Responsible for building a certain table view section.
Can host several row builders.
*/
public class TableSectionBuilder {
private var builders = [RowBuilder]()
public var headerTitle: String?
public var footerTitle: String?
public var headerView: UIView?
public var headerHeight: CGFloat = UITableViewAutomaticDimension
public var footerView: UIView?
public var footerHeight: CGFloat = UITableViewAutomaticDimension
/// A total number of rows in section of each row builder.
public var numberOfRowsInSection: Int {
return builders.reduce(0) { $0 + $1.numberOfRows }
}
public init(headerTitle: String? = nil, footerTitle: String? = nil, rowBuilders: [RowBuilder]? = nil) {
self.headerTitle = headerTitle
self.footerTitle = footerTitle
if let initialRows = rowBuilders {
self.builders.appendContentsOf(initialRows)
}
}
public init(headerView: UIView? = nil, headerHeight: CGFloat = UITableViewAutomaticDimension, footerView: UIView? = nil, footerHeight: CGFloat = UITableViewAutomaticDimension) {
self.headerView = headerView
self.headerHeight = headerHeight
self.footerView = footerView
self.footerHeight = footerHeight
}
internal func builderAtIndex(var index: Int) -> (RowBuilder, Int)? {
for builder in builders {
if index < builder.numberOfRows {
return (builder, index)
}
index -= builder.numberOfRows
}
return nil
}
}

118
Tablet/Tablet.swift Normal file
View File

@ -0,0 +1,118 @@
//
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import UIKit
import Foundation
internal let kActionPerformedNotificationKey = "_action"
/**
The actions that Tablet provides.
*/
public enum ActionType {
case click
case select
case deselect
case configure
case willDisplay
case shouldHighlight
case height
case custom(String)
var key: String {
switch (self) {
case .custom(let key):
return key
default:
return "_\(self)"
}
}
}
public class ActionData<I, C> {
public let cell: C?
public let item: I
public let itemIndex: Int
public let indexPath: NSIndexPath
init(cell: C?, indexPath: NSIndexPath, item: I, itemIndex: Int) {
self.cell = cell
self.indexPath = indexPath
self.item = item
self.itemIndex = itemIndex
}
}
/**
A custom action that you can trigger from your cell.
You can eacily catch actions using a chaining manner with your row builder.
*/
public class Action {
/// The cell that triggers an action.
public let cell: UITableViewCell
/// The action unique key.
public let key: String
/// The custom user info.
public let userInfo: [NSObject: AnyObject]?
public init(key: String, sender: UITableViewCell, userInfo: [NSObject: AnyObject]? = nil) {
self.key = key
self.cell = sender
self.userInfo = userInfo
}
public func perform() {
NSNotificationCenter.defaultCenter().postNotificationName(kActionPerformedNotificationKey, object: self)
}
}
/**
If you want to delegate your cell configuration logic to cell itself (with your view model or even model) than
just provide an implementation of this protocol for your cell. Enjoy safe-typisation.
*/
public protocol ConfigurableCell {
typealias Item
static func reusableIdentifier() -> String
func configureWithItem(item: Item)
}
/**
A protocol that every row builder should follow.
A certain section can only works with row builders that respect this protocol.
*/
public protocol RowBuilder {
var numberOfRows: Int { get }
var reusableIdentifier: String { get }
var estimatedRowHeight: CGFloat { get }
func performAction(actionType: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int) -> AnyObject?
}

View File

@ -0,0 +1,333 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
508B71841BF48DD300272920 /* TableSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508B71831BF48DD300272920 /* TableSectionBuilder.swift */; };
508B71861BF48E0D00272920 /* TableRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 508B71851BF48E0D00272920 /* TableRowBuilder.swift */; };
DA1BCD0F1BF5472C00CC0479 /* TableDirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */; };
DA1BCD111BF7388C00CC0479 /* CustomTableActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA1BCD101BF7388C00CC0479 /* CustomTableActions.swift */; };
DAB7EB2B1BEF787300D2AD5E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB2A1BEF787300D2AD5E /* AppDelegate.swift */; };
DAB7EB2D1BEF787300D2AD5E /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */; };
DAB7EB301BEF787300D2AD5E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */; };
DAB7EB321BEF787300D2AD5E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DAB7EB311BEF787300D2AD5E /* Assets.xcassets */; };
DAB7EB351BEF787300D2AD5E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */; };
DAB7EB3E1BEF78A400D2AD5E /* Tablet.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */; };
DAB7EB401BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
508B71831BF48DD300272920 /* TableSectionBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSectionBuilder.swift; sourceTree = "<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>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
DAB7EB241BEF787300D2AD5E /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
DAB7EB1E1BEF787300D2AD5E = {
isa = PBXGroup;
children = (
DAB7EB3C1BEF789500D2AD5E /* Tablet */,
DAB7EB291BEF787300D2AD5E /* TabletDemo */,
DAB7EB281BEF787300D2AD5E /* Products */,
);
sourceTree = "<group>";
};
DAB7EB281BEF787300D2AD5E /* Products */ = {
isa = PBXGroup;
children = (
DAB7EB271BEF787300D2AD5E /* TabletDemo.app */,
);
name = Products;
sourceTree = "<group>";
};
DAB7EB291BEF787300D2AD5E /* TabletDemo */ = {
isa = PBXGroup;
children = (
DAB7EB2A1BEF787300D2AD5E /* AppDelegate.swift */,
DAB7EB2C1BEF787300D2AD5E /* ViewController.swift */,
DAB7EB3F1BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift */,
DA1BCD101BF7388C00CC0479 /* CustomTableActions.swift */,
DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */,
DAB7EB311BEF787300D2AD5E /* Assets.xcassets */,
DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */,
DAB7EB361BEF787300D2AD5E /* Info.plist */,
);
path = TabletDemo;
sourceTree = "<group>";
};
DAB7EB3C1BEF789500D2AD5E /* Tablet */ = {
isa = PBXGroup;
children = (
DAB7EB3D1BEF78A400D2AD5E /* Tablet.swift */,
DA1BCD0E1BF5472C00CC0479 /* TableDirector.swift */,
508B71851BF48E0D00272920 /* TableRowBuilder.swift */,
508B71831BF48DD300272920 /* TableSectionBuilder.swift */,
);
name = Tablet;
path = ../Tablet;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
DAB7EB261BEF787300D2AD5E /* TabletDemo */ = {
isa = PBXNativeTarget;
buildConfigurationList = DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TabletDemo" */;
buildPhases = (
DAB7EB231BEF787300D2AD5E /* Sources */,
DAB7EB241BEF787300D2AD5E /* Frameworks */,
DAB7EB251BEF787300D2AD5E /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = TabletDemo;
productName = TabletDemo;
productReference = DAB7EB271BEF787300D2AD5E /* TabletDemo.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
DAB7EB1F1BEF787300D2AD5E /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = Tablet;
TargetAttributes = {
DAB7EB261BEF787300D2AD5E = {
CreatedOnToolsVersion = 7.0.1;
DevelopmentTeam = Z48R734SJX;
};
};
};
buildConfigurationList = DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TabletDemo" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = DAB7EB1E1BEF787300D2AD5E;
productRefGroup = DAB7EB281BEF787300D2AD5E /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
DAB7EB261BEF787300D2AD5E /* TabletDemo */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
DAB7EB251BEF787300D2AD5E /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DAB7EB351BEF787300D2AD5E /* LaunchScreen.storyboard in Resources */,
DAB7EB321BEF787300D2AD5E /* Assets.xcassets in Resources */,
DAB7EB301BEF787300D2AD5E /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
DAB7EB231BEF787300D2AD5E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
508B71841BF48DD300272920 /* TableSectionBuilder.swift in Sources */,
DAB7EB2D1BEF787300D2AD5E /* ViewController.swift in Sources */,
DAB7EB3E1BEF78A400D2AD5E /* Tablet.swift in Sources */,
508B71861BF48E0D00272920 /* TableRowBuilder.swift in Sources */,
DA1BCD0F1BF5472C00CC0479 /* TableDirector.swift in Sources */,
DAB7EB401BEFD07E00D2AD5E /* ConfigurableTableViewCell.swift in Sources */,
DAB7EB2B1BEF787300D2AD5E /* AppDelegate.swift in Sources */,
DA1BCD111BF7388C00CC0479 /* CustomTableActions.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
DAB7EB2E1BEF787300D2AD5E /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
DAB7EB2F1BEF787300D2AD5E /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
DAB7EB331BEF787300D2AD5E /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
DAB7EB341BEF787300D2AD5E /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
DAB7EB371BEF787300D2AD5E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
DAB7EB381BEF787300D2AD5E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
name = Release;
};
DAB7EB3A1BEF787300D2AD5E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
INFOPLIST_FILE = TabletDemo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.TabletDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
};
name = Debug;
};
DAB7EB3B1BEF787300D2AD5E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
INFOPLIST_FILE = TabletDemo/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablet.TabletDemo;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
DAB7EB221BEF787300D2AD5E /* Build configuration list for PBXProject "TabletDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DAB7EB371BEF787300D2AD5E /* Debug */,
DAB7EB381BEF787300D2AD5E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
DAB7EB391BEF787300D2AD5E /* Build configuration list for PBXNativeTarget "TabletDemo" */ = {
isa = XCConfigurationList;
buildConfigurations = (
DAB7EB3A1BEF787300D2AD5E /* Debug */,
DAB7EB3B1BEF787300D2AD5E /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = DAB7EB1F1BEF787300D2AD5E /* Project object */;
}

View File

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

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.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">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,22 @@
<?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>TabletDemo.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>DAB7EB261BEF787300D2AD5E</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

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,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.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">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "DAB7EB261BEF787300D2AD5E"
BuildableName = "TabletDemo.app"
BlueprintName = "TabletDemo"
ReferencedContainer = "container:TabletDemo.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,22 @@
<?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>TabletDemo.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>DAB7EB261BEF787300D2AD5E</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,15 @@
//
// AppDelegate.swift
// TabletDemo
//
// Created by Max Sokolov on 08/11/15.
// Copyright © 2015 Tablet. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}

View File

@ -0,0 +1,38 @@
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -0,0 +1,28 @@
<?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">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="8154"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
</layoutGuides>
<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>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,130 @@
<?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" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<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" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<animations/>
</tableViewCellContentView>
<animations/>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="configurable_cell" 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" cocoaTouchSystemColor="darkTextColor"/>
<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

@ -0,0 +1,34 @@
//
// ConfigurableTableViewCell.swift
// TabletDemo
//
// Created by Max Sokolov on 08/11/15.
// Copyright © 2015 Tablet. All rights reserved.
//
import UIKit
let kConfigurableTableViewCellButtonClickedAction = "button_clicked"
class ConfigurableTableViewCell: UITableViewCell, ConfigurableCell {
typealias Item = String
@IBOutlet weak var button: UIButton!
@IBOutlet weak var contentLabel: UILabel!
static func reusableIdentifier() -> String {
return "configurable_cell"
}
func configureWithItem(item: Item) {
button.setTitle("Button \(item)", forState: .Normal)
}
@IBAction func buttonClicked(sender: UIButton) {
Action(key: kConfigurableTableViewCellButtonClickedAction, sender: self).perform()
}
}

View File

@ -0,0 +1,20 @@
//
// CustomTableActions.swift
// TabletDemo
//
// Created by Max Sokolov on 14/11/15.
// Copyright © 2015 Tablet. All rights reserved.
//
import UIKit
import Foundation
let kTableDirectorDidEndDisplayingCell = "enddisplaycell"
extension TableDirector {
public func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
performAction(.custom(kTableDirectorDidEndDisplayingCell), cell: cell, indexPath: indexPath)
}
}

View File

@ -0,0 +1,38 @@
<?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>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
</dict>
</plist>

View File

@ -0,0 +1,60 @@
//
// ViewController.swift
// TabletDemo
//
// Created by Max Sokolov on 08/11/15.
// Copyright © 2015 Tablet. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var tableDirector: TableDirector!
override func viewDidLoad() {
super.viewDidLoad()
tableDirector = TableDirector(tableView: tableView)
let rowBuilder = TableRowBuilder<Int, UITableViewCell>(items: [1, 2, 3, 4], id: "cell")
.action(.configure) { data in
data.cell?.textLabel?.text = "\(data.item)"
}
.action(.shouldHighlight) { data in
return false
}
.action(kTableDirectorDidEndDisplayingCell) { data -> Void in
print("end display: \(data.indexPath)")
}
let configurableRowBuilder = TableConfigurableRowBuilder<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 sectionBuilder = TableSectionBuilder(headerTitle: "Tablet", footerTitle: "Deal with table view like a boss.", rowBuilders: [rowBuilder, configurableRowBuilder])
tableDirector.appendSection(sectionBuilder)
}
}