Merge pull request #44 from maxsokolov/swift/3.0

Swift 3.0 support
This commit is contained in:
Max Sokolov 2016-10-03 16:09:50 +04:00 committed by GitHub
commit 81fbd2f5e5
19 changed files with 238 additions and 201 deletions

View File

@ -1,5 +1,5 @@
language: objective-c
osx_image: xcode7.3
osx_image: xcode8
branches:
only:
- master
@ -7,20 +7,20 @@ env:
global:
- LC_CTYPE=en_US.UTF-8
- LANG=en_US.UTF-8
- IOS_SDK=iphonesimulator9.3
- IOS_SDK=iphonesimulator10.0
- SCHEME_IOS="TableKit"
- PROJECT_FRAMEWORK="TableKit.xcodeproj"
matrix:
- DESTINATION="OS=8.1,name=iPhone 4S" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=8.2,name=iPhone 5" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=9.0,name=iPhone 6" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=9.1,name=iPhone 6 Plus" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=9.2,name=iPhone 6S" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=9.3,name=iPhone 6S Plus" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=10.0,name=iPhone 5" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
- DESTINATION="OS=10.0,name=iPhone 7 Plus" SCHEME="$SCHEME_IOS" SDK="$IOS_SDK"
script:
- set -o pipefail
- xcodebuild -version
- xcodebuild -showsdks
- xcodebuild -project "$PROJECT_FRAMEWORK" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES test | xcpretty -c;
- xcodebuild -project "$PROJECT_FRAMEWORK" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES test | xcpretty;

View File

@ -25,17 +25,17 @@ class MainController: UIViewController {
let clickAction = TableRowAction<String, ConfigurableTableViewCell>(.click) { [weak self] (data) in
switch data.indexPath.row {
switch (data.indexPath as NSIndexPath).row {
case 0:
self?.performSegueWithIdentifier("autolayoutcells", sender: nil)
self?.performSegue(withIdentifier: "autolayoutcells", sender: nil)
case 1:
self?.performSegueWithIdentifier("nibcells", sender: nil)
self?.performSegue(withIdentifier: "nibcells", sender: nil)
default:
break
}
}
let rows: [Row] = [
let rows = [
TableRow<String, ConfigurableTableViewCell>(item: "Autolayout cells", actions: [clickAction]),
TableRow<String, ConfigurableTableViewCell>(item: "Nib cells", actions: [clickAction])
@ -44,4 +44,4 @@ class MainController: UIViewController {
// automatically creates a section, also could be used like tableDirector.append(rows: rows)
tableDirector += rows
}
}
}

View File

@ -28,6 +28,6 @@ class NibCellsController: UITableViewController {
let rows: [Row] = numbers.map { TableRow<Int, NibTableViewCell>(item: $0, actions: [shouldHighlightAction]) }
tableDirector.append(rows: rows)
_ = tableDirector.append(rows: rows)
}
}
}

View File

@ -13,7 +13,7 @@ class ConfigurableTableViewCell: UITableViewCell, ConfigurableCell {
func configure(with text: String) {
accessoryType = .DisclosureIndicator
accessoryType = .disclosureIndicator
textLabel?.text = text
}
}
}

View File

@ -227,12 +227,13 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0720;
LastUpgradeCheck = 0700;
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = Tablet;
TargetAttributes = {
DAB7EB261BEF787300D2AD5E = {
CreatedOnToolsVersion = 7.0.1;
DevelopmentTeam = Z48R734SJX;
LastSwiftMigration = 0800;
};
};
};
@ -330,8 +331,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -374,8 +377,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -394,6 +399,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
VALIDATE_PRODUCT = YES;
};
name = Release;
@ -401,32 +407,34 @@
DAB7EB3A1BEF787300D2AD5E /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
INFOPLIST_FILE = Resources/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo;
PRODUCT_NAME = TableKitDemo;
PROVISIONING_PROFILE = "";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
DAB7EB3B1BEF787300D2AD5E /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
INFOPLIST_FILE = Resources/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.demo;
PRODUCT_NAME = TableKitDemo;
PROVISIONING_PROFILE = "";
SWIFT_VERSION = 3.0;
};
name = Release;
};

View File

@ -2,9 +2,9 @@
<p align="left">
<a href="https://travis-ci.org/maxsokolov/TableKit"><img src="https://api.travis-ci.org/maxsokolov/TableKit.svg" alt="Build Status" /></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>
<a href="https://developer.apple.com/swift"><img src="https://img.shields.io/badge/Swift_3.0-compatible-4BC51D.svg?style=flat" alt="Swift 2.2 compatible" /></a>
<a href="https://github.com/Carthage/Carthage"><img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage compatible" /></a>
<a href="https://cocoapods.org/pods/tablekit"><img src="https://img.shields.io/badge/pod-1.2.0-blue.svg" alt="CocoaPods compatible" /></a>
<a href="https://cocoapods.org/pods/tablekit"><img src="https://img.shields.io/badge/pod-1.3.0-blue.svg" alt="CocoaPods compatible" /></a>
<img src="https://img.shields.io/badge/platform-iOS-blue.svg?style=flat" alt="Platform iOS" />
<a href="https://raw.githubusercontent.com/maxsokolov/tablekit/master/LICENSE"><img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License: MIT" /></a>
</p>
@ -177,7 +177,7 @@ let click = TableRowAction<String, UserTableViewCell>(.click) {
}
let rows: [Row] = users.filter({ $0.state == .active }).map({ TableRow<String, UserTableViewCell>(item: $0.name, actions: [click]) })
let rows = users.filter({ $0.state == .active }).map({ TableRow<String, UserTableViewCell>(item: $0.name, actions: [click]) })
tableDirector += rows
```
@ -212,8 +212,8 @@ Clone the repo and drag files from `Sources` folder into your Xcode project.
# Requirements
- iOS 8.0+
- Xcode 7.0+
- iOS 8.0
- Xcode 8.0
# License

View File

@ -35,7 +35,7 @@ public extension ConfigurableCell where Self: UITableViewCell {
static var reuseIdentifier: String {
get {
return String(self)
return String(describing: self)
}
}
@ -52,4 +52,4 @@ public extension ConfigurableCell where Self: UITableViewCell {
return nil
}
}
}
}

View File

@ -22,24 +22,24 @@ import UIKit
public protocol CellHeightCalculatable {
func height(row: Row, path: NSIndexPath) -> CGFloat
func estimatedHeight(row: Row, path: NSIndexPath) -> CGFloat
func height(_ row: Row, path: IndexPath) -> CGFloat
func estimatedHeight(_ row: Row, path: IndexPath) -> CGFloat
func invalidate()
}
public class PrototypeHeightStrategy: CellHeightCalculatable {
open class PrototypeHeightStrategy: CellHeightCalculatable {
private weak var tableView: UITableView?
private var prototypes = [String: UITableViewCell]()
private var cachedHeights = [Int: CGFloat]()
private var separatorHeight = 1 / UIScreen.mainScreen().scale
private var separatorHeight = 1 / UIScreen.main.scale
init(tableView: UITableView?) {
self.tableView = tableView
}
public func height(row: Row, path: NSIndexPath) -> CGFloat {
open func height(_ row: Row, path: IndexPath) -> CGFloat {
guard let tableView = tableView else { return 0 }
@ -52,7 +52,7 @@ public class PrototypeHeightStrategy: CellHeightCalculatable {
var prototypeCell = prototypes[row.reuseIdentifier]
if prototypeCell == nil {
prototypeCell = tableView.dequeueReusableCellWithIdentifier(row.reuseIdentifier)
prototypeCell = tableView.dequeueReusableCell(withIdentifier: row.reuseIdentifier)
prototypes[row.reuseIdentifier] = prototypeCell
}
@ -60,18 +60,18 @@ public class PrototypeHeightStrategy: CellHeightCalculatable {
row.configure(cell)
cell.bounds = CGRectMake(0, 0, tableView.bounds.size.width, cell.bounds.height)
cell.bounds = CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: cell.bounds.height)
cell.setNeedsLayout()
cell.layoutIfNeeded()
let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + (tableView.separatorStyle != .None ? separatorHeight : 0)
let height = cell.contentView.systemLayoutSizeFitting(UILayoutFittingCompressedSize).height + (tableView.separatorStyle != .none ? separatorHeight : 0)
cachedHeights[hash] = height
return height
}
public func estimatedHeight(row: Row, path: NSIndexPath) -> CGFloat {
open func estimatedHeight(_ row: Row, path: IndexPath) -> CGFloat {
guard let tableView = tableView else { return 0 }
@ -81,14 +81,14 @@ public class PrototypeHeightStrategy: CellHeightCalculatable {
return height
}
if let estimatedHeight = row.estimatedHeight where estimatedHeight > 0 {
if let estimatedHeight = row.estimatedHeight , estimatedHeight > 0 {
return estimatedHeight
}
return UITableViewAutomaticDimension
}
public func invalidate() {
open func invalidate() {
cachedHeights.removeAll()
}
}
}

View File

@ -20,27 +20,27 @@
// --
public func +=(left: TableDirector, right: TableSection) {
left.append(section: right)
_ = left.append(section: right)
}
public func +=(left: TableDirector, right: [TableSection]) {
left.append(sections: right)
_ = left.append(sections: right)
}
// --
public func +=(left: TableDirector, right: Row) {
left.append(sections: [TableSection(rows: [right])])
_ = left.append(sections: [TableSection(rows: [right])])
}
public func +=(left: TableDirector, right: [Row]) {
left.append(sections: [TableSection(rows: right)])
_ = left.append(sections: [TableSection(rows: right)])
}
// --
public func +=(left: TableSection, right: Row) {
left.append(row: right)
_ = left.append(row: right)
}
public func +=(left: TableSection, right: [Row]) {
left.append(rows: right)
}
_ = left.append(rows: right)
}

View File

@ -28,25 +28,25 @@ struct TableKitNotifications {
A custom action that you can trigger from your cell.
You can easily catch actions using a chaining manner with your row.
*/
public class TableCellAction {
open class TableCellAction {
/// The cell that triggers an action.
public let cell: UITableViewCell
open let cell: UITableViewCell
/// The action unique key.
public let key: String
open let key: String
/// The custom user info.
public let userInfo: [NSObject: AnyObject]?
open let userInfo: [AnyHashable: Any]?
public init(key: String, sender: UITableViewCell, userInfo: [NSObject: AnyObject]? = nil) {
public init(key: String, sender: UITableViewCell, userInfo: [AnyHashable: Any]? = nil) {
self.key = key
self.cell = sender
self.userInfo = userInfo
}
public func invoke() {
NSNotificationCenter.defaultCenter().postNotificationName(TableKitNotifications.CellAction, object: self, userInfo: userInfo)
open func invoke() {
NotificationCenter.default.post(name: Notification.Name(rawValue: TableKitNotifications.CellAction), object: self, userInfo: userInfo)
}
}
}

View File

@ -29,30 +29,30 @@ class TableCellRegisterer {
self.tableView = tableView
}
func register(cellType cellType: AnyClass, forCellReuseIdentifier reuseIdentifier: String) {
func register(cellType: AnyClass, forCellReuseIdentifier reuseIdentifier: String) {
if registeredIds.contains(reuseIdentifier) {
return
}
// check if cell is already registered, probably cell has been registered by storyboard
if tableView?.dequeueReusableCellWithIdentifier(reuseIdentifier) != nil {
if tableView?.dequeueReusableCell(withIdentifier: reuseIdentifier) != nil {
registeredIds.insert(reuseIdentifier)
return
}
let bundle = NSBundle(forClass: cellType)
let bundle = Bundle(for: cellType)
// we hope that cell's xib file has name that equals to cell's class name
// in that case we could register nib
if let _ = bundle.pathForResource(reuseIdentifier, ofType: "nib") {
tableView?.registerNib(UINib(nibName: reuseIdentifier, bundle: bundle), forCellReuseIdentifier: reuseIdentifier)
if let _ = bundle.path(forResource: reuseIdentifier, ofType: "nib") {
tableView?.register(UINib(nibName: reuseIdentifier, bundle: bundle), forCellReuseIdentifier: reuseIdentifier)
// otherwise, register cell class
} else {
tableView?.registerClass(cellType, forCellReuseIdentifier: reuseIdentifier)
tableView?.register(cellType, forCellReuseIdentifier: reuseIdentifier)
}
registeredIds.insert(reuseIdentifier)
}
}
}

View File

@ -23,16 +23,16 @@ import UIKit
/**
Responsible for table view's datasource and delegate.
*/
public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
public private(set) weak var tableView: UITableView?
public private(set) var sections = [TableSection]()
open private(set) weak var tableView: UITableView?
open private(set) var sections = [TableSection]()
private weak var scrollDelegate: UIScrollViewDelegate?
private var heightStrategy: CellHeightCalculatable?
private var cellRegisterer: TableCellRegisterer?
public var shouldUsePrototypeCellHeightCalculation: Bool = false {
open var shouldUsePrototypeCellHeightCalculation: Bool = false {
didSet {
if shouldUsePrototypeCellHeightCalculation {
heightStrategy = PrototypeHeightStrategy(tableView: tableView)
@ -40,7 +40,7 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
}
}
public var isEmpty: Bool {
open var isEmpty: Bool {
return sections.isEmpty
}
@ -56,57 +56,57 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
self.tableView?.delegate = self
self.tableView?.dataSource = self
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(didReceiveAction), name: TableKitNotifications.CellAction, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveAction), name: NSNotification.Name(rawValue: TableKitNotifications.CellAction), object: nil)
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
NotificationCenter.default.removeObserver(self)
}
public func reload() {
open func reload() {
tableView?.reloadData()
}
// MARK: Public
public func invoke(action action: TableRowActionType, cell: UITableViewCell?, indexPath: NSIndexPath) -> Any? {
return sections[indexPath.section].rows[indexPath.row].invoke(action, cell: cell, path: indexPath)
open func invoke(action: TableRowActionType, cell: UITableViewCell?, indexPath: IndexPath) -> Any? {
return sections[(indexPath as NSIndexPath).section].rows[(indexPath as NSIndexPath).row].invoke(action, cell: cell, path: indexPath)
}
public override func respondsToSelector(selector: Selector) -> Bool {
return super.respondsToSelector(selector) || scrollDelegate?.respondsToSelector(selector) == true
open override func responds(to selector: Selector) -> Bool {
return super.responds(to: selector) || scrollDelegate?.responds(to: selector) == true
}
public override func forwardingTargetForSelector(selector: Selector) -> AnyObject? {
return scrollDelegate?.respondsToSelector(selector) == true ? scrollDelegate : super.forwardingTargetForSelector(selector)
open override func forwardingTarget(for selector: Selector) -> Any? {
return scrollDelegate?.responds(to: selector) == true ? scrollDelegate : super.forwardingTarget(for: selector)
}
// MARK: - Internal -
func hasAction(action: TableRowActionType, atIndexPath indexPath: NSIndexPath) -> Bool {
return sections[indexPath.section].rows[indexPath.row].hasAction(action)
func hasAction(_ action: TableRowActionType, atIndexPath indexPath: IndexPath) -> Bool {
return sections[(indexPath as NSIndexPath).section].rows[(indexPath as NSIndexPath).row].hasAction(action)
}
func didReceiveAction(notification: NSNotification) {
func didReceiveAction(_ notification: Notification) {
guard let action = notification.object as? TableCellAction, indexPath = tableView?.indexPathForCell(action.cell) else { return }
invoke(action: .custom(action.key), cell: action.cell, indexPath: indexPath)
guard let action = notification.object as? TableCellAction, let indexPath = tableView?.indexPath(for: action.cell) else { return }
_ = invoke(action: .custom(action.key), cell: action.cell, indexPath: indexPath)
}
// MARK: - Height
public func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let row = sections[indexPath.section].rows[indexPath.row]
let row = sections[(indexPath as NSIndexPath).section].rows[(indexPath as NSIndexPath).row]
cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier)
return row.estimatedHeight ?? heightStrategy?.estimatedHeight(row, path: indexPath) ?? UITableViewAutomaticDimension
}
public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let row = sections[indexPath.section].rows[indexPath.row]
let row = sections[(indexPath as NSIndexPath).section].rows[(indexPath as NSIndexPath).row]
let rowHeight = invoke(action: .height, cell: nil, indexPath: indexPath) as? CGFloat
@ -115,57 +115,57 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
// MARK: UITableViewDataSource - configuration
public func numberOfSectionsInTableView(tableView: UITableView) -> Int {
open func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
public func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
open func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].numberOfRows
}
public func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let row = sections[indexPath.section].rows[indexPath.row]
let cell = tableView.dequeueReusableCellWithIdentifier(row.reuseIdentifier, forIndexPath: indexPath)
let row = sections[(indexPath as NSIndexPath).section].rows[(indexPath as NSIndexPath).row]
let cell = tableView.dequeueReusableCell(withIdentifier: row.reuseIdentifier, for: indexPath)
if cell.frame.size.width != tableView.frame.size.width {
cell.frame = CGRectMake(0, 0, tableView.frame.size.width, cell.frame.size.height)
cell.frame = CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: cell.frame.size.height)
cell.layoutIfNeeded()
}
row.configure(cell)
invoke(action: .configure, cell: cell, indexPath: indexPath)
_ = invoke(action: .configure, cell: cell, indexPath: indexPath)
return cell
}
// MARK: UITableViewDataSource - section setup
public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].headerTitle
}
public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
open 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? {
open func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return sections[section].headerView
}
public func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
open func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
return sections[section].footerView
}
public func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
open func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
let section = sections[section]
return section.headerHeight ?? section.headerView?.frame.size.height ?? 0
}
public func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
let section = sections[section]
return section.footerHeight ?? section.footerView?.frame.size.height ?? 0
@ -173,89 +173,89 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
// MARK: UITableViewDelegate - actions
public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
let cell = tableView.cellForRow(at: indexPath)
if invoke(action: .click, cell: cell, indexPath: indexPath) != nil {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
tableView.deselectRow(at: indexPath, animated: true)
} else {
invoke(action: .select, cell: cell, indexPath: indexPath)
_ = invoke(action: .select, cell: cell, indexPath: indexPath)
}
}
public func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
invoke(action: .deselect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath)
open func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
_ = invoke(action: .deselect, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath)
}
public func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
invoke(action: .willDisplay, cell: cell, indexPath: indexPath)
open func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
_ = invoke(action: .willDisplay, cell: cell, indexPath: indexPath)
}
public func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return invoke(action: .shouldHighlight, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? Bool ?? true
open func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
return invoke(action: .shouldHighlight, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? Bool ?? true
}
public func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
open func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
if hasAction(.willSelect, atIndexPath: indexPath) {
return invoke(action: .willSelect, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath) as? NSIndexPath
return invoke(action: .willSelect, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath) as? IndexPath
}
return indexPath
}
// MARK: - Row editing -
public func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return sections[indexPath.section].rows[indexPath.row].isEditingAllowed(forIndexPath: indexPath)
open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return sections[(indexPath as NSIndexPath).section].rows[(indexPath as NSIndexPath).row].isEditingAllowed(forIndexPath: indexPath)
}
public func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
return sections[indexPath.section].rows[indexPath.row].editingActions
open func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
return sections[(indexPath as NSIndexPath).section].rows[(indexPath as NSIndexPath).row].editingActions
}
public func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
open func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .Delete {
invoke(action: .clickDelete, cell: tableView.cellForRowAtIndexPath(indexPath), indexPath: indexPath)
if editingStyle == .delete {
_ = invoke(action: .clickDelete, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath)
}
}
// MARK: - Sections manipulation -
public func append(section section: TableSection) -> Self {
open func append(section: TableSection) -> Self {
append(sections: [section])
_ = append(sections: [section])
return self
}
public func append(sections sections: [TableSection]) -> Self {
open func append(sections: [TableSection]) -> Self {
self.sections.appendContentsOf(sections)
self.sections.append(contentsOf: sections)
return self
}
public func append(rows rows: [Row]) -> Self {
open func append(rows: [Row]) -> Self {
append(section: TableSection(rows: rows))
_ = append(section: TableSection(rows: rows))
return self
}
public func insert(section section: TableSection, atIndex index: Int) -> Self {
open func insert(section: TableSection, atIndex index: Int) -> Self {
sections.insert(section, atIndex: index)
sections.insert(section, at: index)
return self
}
public func delete(index index: Int) -> Self {
open func delete(index: Int) -> Self {
sections.removeAtIndex(index)
sections.remove(at: index)
return self
}
public func clear() -> Self {
open func clear() -> Self {
sections.removeAll()
return self
}
}
}

View File

@ -22,16 +22,16 @@ import UIKit
public protocol RowConfigurable {
func configure(cell: UITableViewCell)
func configure(_ cell: UITableViewCell)
}
public protocol RowActionable {
var editingActions: [UITableViewRowAction]? { get }
func isEditingAllowed(forIndexPath indexPath: NSIndexPath) -> Bool
func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool
func invoke(action: TableRowActionType, cell: UITableViewCell?, path: NSIndexPath) -> Any?
func hasAction(action: TableRowActionType) -> Bool
func invoke(_ action: TableRowActionType, cell: UITableViewCell?, path: IndexPath) -> Any?
func hasAction(_ action: TableRowActionType) -> Bool
}
public protocol RowHashable {
@ -48,29 +48,29 @@ public protocol Row: RowConfigurable, RowActionable, RowHashable {
var defaultHeight: CGFloat? { get }
}
public class TableRow<ItemType, CellType: ConfigurableCell where CellType.T == ItemType, CellType: UITableViewCell>: Row {
open class TableRow<ItemType, CellType: ConfigurableCell>: Row where CellType.T == ItemType, CellType: UITableViewCell {
public let item: ItemType
open let item: ItemType
private lazy var actions = [String: TableRowAction<ItemType, CellType>]()
private(set) public var editingActions: [UITableViewRowAction]?
private(set) open var editingActions: [UITableViewRowAction]?
public var hashValue: Int {
open var hashValue: Int {
return ObjectIdentifier(self).hashValue
}
public var reuseIdentifier: String {
open var reuseIdentifier: String {
return CellType.reuseIdentifier
}
public var estimatedHeight: CGFloat? {
open var estimatedHeight: CGFloat? {
return CellType.estimatedHeight
}
public var defaultHeight: CGFloat? {
open var defaultHeight: CGFloat? {
return CellType.defaultHeight
}
public var cellType: AnyClass {
open var cellType: AnyClass {
return CellType.self
}
@ -83,21 +83,21 @@ public class TableRow<ItemType, CellType: ConfigurableCell where CellType.T == I
// MARK: - RowConfigurable -
public func configure(cell: UITableViewCell) {
open func configure(_ cell: UITableViewCell) {
(cell as? CellType)?.configure(with: item)
}
// MARK: - RowActionable -
public func invoke(action: TableRowActionType, cell: UITableViewCell?, path: NSIndexPath) -> Any? {
open func invoke(_ action: TableRowActionType, cell: UITableViewCell?, path: IndexPath) -> Any? {
return actions[action.key]?.invoke(item: item, cell: cell, path: path)
}
public func hasAction(action: TableRowActionType) -> Bool {
open func hasAction(_ action: TableRowActionType) -> Bool {
return actions[action.key] != nil
}
public func isEditingAllowed(forIndexPath indexPath: NSIndexPath) -> Bool {
open func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool {
if actions[TableRowActionType.canEdit.key] != nil {
return invoke(.canEdit, cell: nil, path: indexPath) as? Bool ?? false
@ -107,15 +107,15 @@ public class TableRow<ItemType, CellType: ConfigurableCell where CellType.T == I
// MARK: - actions -
public func action(action: TableRowAction<ItemType, CellType>) -> Self {
open func action(_ action: TableRowAction<ItemType, CellType>) -> Self {
actions[action.type.key] = action
return self
}
public func action<T>(type: TableRowActionType, handler: (data: TableRowActionData<ItemType, CellType>) -> T) -> Self {
open func action<T>(_ type: TableRowActionType, handler: @escaping (_ data: TableRowActionData<ItemType, CellType>) -> T) -> Self {
actions[type.key] = TableRowAction(type, handler: handler)
actions[type.key] = TableRowAction<ItemType, CellType>(type, handler: handler)
return self
}
}
}

View File

@ -45,14 +45,14 @@ public enum TableRowActionType {
}
}
public class TableRowActionData<ItemType, CellType: ConfigurableCell where CellType.T == ItemType, CellType: UITableViewCell> {
open class TableRowActionData<ItemType, CellType: ConfigurableCell> where CellType.T == ItemType, CellType: UITableViewCell {
public let item: ItemType
public let cell: CellType?
public let indexPath: NSIndexPath
public let userInfo: [NSObject: AnyObject]?
open let item: ItemType
open let cell: CellType?
open let indexPath: IndexPath
open let userInfo: [AnyHashable: Any]?
init(item: ItemType, cell: CellType?, path: NSIndexPath, userInfo: [NSObject: AnyObject]?) {
init(item: ItemType, cell: CellType?, path: IndexPath, userInfo: [AnyHashable: Any]?) {
self.item = item
self.cell = cell
@ -61,24 +61,40 @@ public class TableRowActionData<ItemType, CellType: ConfigurableCell where CellT
}
}
public class TableRowAction<ItemType, CellType: ConfigurableCell where CellType.T == ItemType, CellType: UITableViewCell> {
private enum TableRowActionHandler<ItemType, CellType: ConfigurableCell> where CellType.T == ItemType, CellType: UITableViewCell {
public let type: TableRowActionType
private let handler: ((data: TableRowActionData<ItemType, CellType>) -> Any?)
case voidAction((TableRowActionData<ItemType, CellType>) -> Void)
case action((TableRowActionData<ItemType, CellType>) -> Any?)
public init(_ type: TableRowActionType, handler: (data: TableRowActionData<ItemType, CellType>) -> Void) {
func invoke(item: ItemType, cell: UITableViewCell?, path: IndexPath) -> Any? {
switch self {
case .voidAction(let handler):
return handler(TableRowActionData(item: item, cell: cell as? CellType, path: path, userInfo: nil))
case .action(let handler):
return handler(TableRowActionData(item: item, cell: cell as? CellType, path: path, userInfo: nil))
}
}
}
open class TableRowAction<ItemType, CellType: ConfigurableCell> where CellType.T == ItemType, CellType: UITableViewCell {
open let type: TableRowActionType
private let handler: TableRowActionHandler<ItemType, CellType>
public init(_ type: TableRowActionType, handler: @escaping (_ data: TableRowActionData<ItemType, CellType>) -> Void) {
self.type = type
self.handler = handler
self.handler = .voidAction(handler)
}
public init<T>(_ type: TableRowActionType, handler: (data: TableRowActionData<ItemType, CellType>) -> T) {
public init<T>(_ type: TableRowActionType, handler: @escaping (_ data: TableRowActionData<ItemType, CellType>) -> T) {
self.type = type
self.handler = handler
self.handler = .action(handler)
}
func invoke(item item: ItemType, cell: UITableViewCell?, path: NSIndexPath) -> Any? {
return handler(data: TableRowActionData(item: item, cell: cell as? CellType, path: path, userInfo: nil))
func invoke(item: ItemType, cell: UITableViewCell?, path: IndexPath) -> Any? {
return handler.invoke(item: item, cell: cell, path: path)
}
}
}

View File

@ -20,31 +20,31 @@
import UIKit
public class TableSection {
open class TableSection {
public private(set) var rows = [Row]()
open private(set) var rows = [Row]()
public var headerTitle: String?
public var footerTitle: String?
open var headerTitle: String?
open var footerTitle: String?
public var headerView: UIView?
public var footerView: UIView?
open var headerView: UIView?
open var footerView: UIView?
public var headerHeight: CGFloat? = nil
public var footerHeight: CGFloat? = nil
open var headerHeight: CGFloat? = nil
open var footerHeight: CGFloat? = nil
public var numberOfRows: Int {
open var numberOfRows: Int {
return rows.count
}
public var isEmpty: Bool {
open var isEmpty: Bool {
return rows.isEmpty
}
public init(rows: [Row]? = nil) {
if let initialRows = rows {
self.rows.appendContentsOf(initialRows)
self.rows.append(contentsOf: initialRows)
}
}
@ -64,31 +64,31 @@ public class TableSection {
// MARK: - Public -
public func clear() {
open func clear() {
rows.removeAll()
}
public func append(row row: Row) {
open func append(row: Row) {
append(rows: [row])
}
public func append(rows rows: [Row]) {
self.rows.appendContentsOf(rows)
open func append(rows: [Row]) {
self.rows.append(contentsOf: rows)
}
public func insert(row row: Row, at index: Int) {
rows.insert(row, atIndex: index)
open func insert(row: Row, at index: Int) {
rows.insert(row, at: index)
}
public func insert(rows rows: [Row], at index: Int) {
self.rows.insertContentsOf(rows, at: index)
open func insert(rows: [Row], at index: Int) {
self.rows.insert(contentsOf: rows, at: index)
}
public func replace(rowAt index: Int, with row: Row) {
open func replace(rowAt index: Int, with row: Row) {
rows[index] = row
}
public func delete(index index: Int) {
rows.removeAtIndex(index)
open func delete(index: Int) {
rows.remove(at: index)
}
}
}

View File

@ -2,10 +2,10 @@ Pod::Spec.new do |s|
s.name = 'TableKit'
s.module_name = 'TableKit'
s.version = '1.2.0'
s.version = '1.3.0'
s.homepage = 'https://github.com/maxsokolov/TableKit'
s.summary = 'Type-safe declarative table views. Swift 2.2 is required.'
s.summary = 'Type-safe declarative table views with Swift.'
s.author = { 'Max Sokolov' => 'i@maxsokolov.net' }
s.license = { :type => 'MIT', :file => 'LICENSE' }

View File

@ -174,14 +174,16 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0730;
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = "Max Sokolov";
TargetAttributes = {
DA9EA7551D0B679A0021F650 = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0800;
};
DA9EA7C31D0EC45F0021F650 = {
CreatedOnToolsVersion = 7.3;
LastSwiftMigration = 0800;
};
};
};
@ -270,8 +272,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -319,8 +323,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@ -340,6 +346,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
@ -351,6 +358,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@ -363,6 +371,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
@ -370,6 +379,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@ -381,6 +391,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 3.0;
};
name = Release;
};
@ -392,6 +403,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Debug;
};
@ -403,6 +415,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.tablekit.TableKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0;
};
name = Release;
};

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0730"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@ -74,7 +74,7 @@ class TabletTests: XCTestCase {
super.setUp()
testController = TestController()
testController.view.hidden = false
testController.view.isHidden = false
}
override func tearDown() {
@ -99,10 +99,10 @@ class TabletTests: XCTestCase {
testController.tableDirector += row
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.numberOfSections?(in: testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section")
let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) as? TestTableViewCell
let cell = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
XCTAssertNotNil(cell)
XCTAssertTrue(cell?.textLabel?.text == data.title)
}
@ -116,12 +116,12 @@ class TabletTests: XCTestCase {
testController.tableDirector += rows
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.numberOfSections?(in: testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == data.count, "Table view should have certain number of rows in a section")
for (index, element) in data.enumerate() {
for (index, element) in data.enumerated() {
let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: index, inSection: 0)) as? TestTableViewCell
let cell = testController.tableView.cellForRow(at: IndexPath(row: index, section: 0)) as? TestTableViewCell
XCTAssertNotNil(cell)
XCTAssertTrue(cell?.textLabel?.text == element.title)
}
@ -139,7 +139,7 @@ class TabletTests: XCTestCase {
testController.tableDirector += section
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.numberOfSections?(in: testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView?(testController.tableView, titleForHeaderInSection: 0) == sectionHeaderTitle)
@ -159,7 +159,7 @@ class TabletTests: XCTestCase {
testController.tableDirector += section
testController.tableView.reloadData()
XCTAssertTrue(testController.tableView.dataSource?.numberOfSectionsInTableView?(testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.numberOfSections?(in: testController.tableView) == 1, "Table view should have a section")
XCTAssertTrue(testController.tableView.dataSource?.tableView(testController.tableView, numberOfRowsInSection: 0) == 1, "Table view should have certain number of rows in a section")
XCTAssertTrue(testController.tableView.delegate?.tableView?(testController.tableView, viewForHeaderInSection: 0) == sectionHeaderView)
@ -168,7 +168,7 @@ class TabletTests: XCTestCase {
func testRowBuilderCustomActionInvokedAndSentUserInfo() {
let expectation = expectationWithDescription("cell action")
let expectation = self.expectation(description: "cell action")
let row = TableRow<TestData, TestTableViewCell>(item: TestData(title: "title"))
.action(TableRowAction(.custom(TestTableViewCellOptions.CellAction)) { (data) in
@ -178,16 +178,16 @@ class TabletTests: XCTestCase {
expectation.fulfill()
})
testController.view.hidden = false
testController.view.isHidden = false
testController.tableDirector += row
testController.tableView.reloadData()
let cell = testController.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0)) as? TestTableViewCell
let cell = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
XCTAssertNotNil(cell, "Cell should exists and should be TestTableViewCell")
cell?.raiseAction()
waitForExpectationsWithTimeout(1.0, handler: nil)
waitForExpectations(timeout: 1.0, handler: nil)
}
}
}