diff --git a/.swiftlint.yml b/.swiftlint.yml
new file mode 100644
index 0000000..085a258
--- /dev/null
+++ b/.swiftlint.yml
@@ -0,0 +1,25 @@
+excluded: # paths to ignore during linting. Takes precedence over `included`.
+ - External
+ - Examples
+opt_in_rules:
+ - empty_count
+ - explicit_init
+ - closure_spacing
+ - overridden_super_call
+ - redundant_nil_coalescing
+ - private_outlet
+ - nimble_operator
+ - attributes
+ - operator_usage_whitespace
+ - closure_end_indentation
+ - first_where
+ - number_separator
+ - prohibited_super_call
+ - fatal_error_message
+identifier_name:
+ excluded:
+ - x
+ - y
+ - id
+ - from
+ - to
diff --git a/.travis.yml b/.travis.yml
index 4ee27f1..4a693b2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,7 @@
language: objective-c
-osx_image: xcode8.1
-script: travis_retry sh build.sh
+osx_image: xcode8.3
+script:
+ - swiftlint
+ - travis_retry sh build.sh
after_success:
- bash <(curl -s https://codecov.io/bash)
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..7229ec3
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,20 @@
+# Change Log
+All notable changes to this project will be documented in this file.
+
+---
+
+## [Unreleased]
+### Added
+- Support `TableViewManager` as `manager` property of `Item` and `Section`
+- Support `UIScrollViewDelegate` from `TableViewManager` via `scrollDelegate`
+
+### Changed
+- Move `UITableViewDataSource` and `UITableViewDelegate` implementations from `TableViewManager` to `TableViewKitDataSource` and `TableViewKitDelegate` which are now the properties: `dataSource` and `delegate`, respectively.
+- Method `Item.section(in:)` has been renamed to `Item.section`
+- Method `Item.indexPath(in:)` has been renamed to `Item.indexPath`
+- Method `Item.reload(in:with:)` has been renamed to `Item.reload(with:)`
+- Method `Item.select(in:animated:scrollPosition:)` has been renamed to `Item.select(animated:scrollPosition:)`
+- Method `Item.select(in:animated:scrollPosition:)` has been renamed to `Item.select(animated:scrollPosition:)`
+- Method `Item.deselect(in:animated:)` has been renamed to `Item.deselect(animated:)`
+- Method `Section.index(in:)` has been renamed to `Section.index`
+
diff --git a/Examples/SampleApp/SampleApp/ActionBar/ActionBar.swift b/Examples/SampleApp/SampleApp/ActionBar/ActionBar.swift
index c8aa814..2a8c505 100644
--- a/Examples/SampleApp/SampleApp/ActionBar/ActionBar.swift
+++ b/Examples/SampleApp/SampleApp/ActionBar/ActionBar.swift
@@ -34,8 +34,8 @@ open class ActionBar: UIToolbar {
fileprivate func setup() {
- let previousButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.init(rawValue: 105)!, target: self, action: #selector(previousHandler))
- let nextButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.init(rawValue: 106)!, target: self, action: #selector(nextHandler))
+ let previousButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem(rawValue: 105)!, target: self, action: #selector(previousHandler))
+ let nextButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem(rawValue: 106)!, target: self, action: #selector(nextHandler))
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(handleActionBarDone))
let spacer = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
diff --git a/Examples/SampleApp/SampleApp/ActionBar/ActionBarManager.swift b/Examples/SampleApp/SampleApp/ActionBar/ActionBarManager.swift
index c68f73d..ab633bd 100644
--- a/Examples/SampleApp/SampleApp/ActionBar/ActionBarManager.swift
+++ b/Examples/SampleApp/SampleApp/ActionBar/ActionBarManager.swift
@@ -22,7 +22,7 @@ class ActionBarManager: ActionBarDelegate {
func isFirstResponder(item: Item) -> Bool {
if isResponder(item: item),
- let indexPath = item.indexPath(in: manager),
+ let indexPath = item.indexPath,
manager.tableView.cellForRow(at: indexPath)?.isFirstResponder == true {
return true
}
@@ -47,7 +47,7 @@ class ActionBarManager: ActionBarDelegate {
item = array.prefix(upTo: index).reversed().first(where: isResponder)
}
- return item?.indexPath(in: manager)
+ return item?.indexPath
}
}
diff --git a/Examples/SampleApp/SampleApp/Example1.swift b/Examples/SampleApp/SampleApp/Example1.swift
index a16559e..9b0b69a 100644
--- a/Examples/SampleApp/SampleApp/Example1.swift
+++ b/Examples/SampleApp/SampleApp/Example1.swift
@@ -42,17 +42,17 @@ class Example1: UIViewController, TableViewManagerCompatible {
textFieldItem2.validation.add(rule: ExistRule())
item.onSelection = { item in
- item.deselect(in: self.vc.tableViewManager, animated: true)
+ item.deselect(animated: true)
self.vc.showPickerControl()
}
dateItem.accessoryType = .disclosureIndicator
dateItem.onSelection = { item in
- item.deselect(in: self.vc.tableViewManager, animated: true)
+ item.deselect(animated: true)
self.vc.showDatePickerControl()
}
selectionItem.accessoryType = .disclosureIndicator
selectionItem.onSelection = { item in
- item.deselect(in: self.vc.tableViewManager, animated: true)
+ item.deselect(animated: true)
self.vc.showPickerControl()
}
states[State.preParty] = [item, dateItem, selectionItem, textFieldItem, textFieldItem2]
@@ -82,7 +82,7 @@ class Example1: UIViewController, TableViewManagerCompatible {
} else {
let item = CustomItem(title: "Label \(index)")
item.onSelection = { item in
- item.deselect(in: self.vc.tableViewManager, animated: true)
+ item.deselect(animated: true)
}
return item
}
@@ -117,7 +117,7 @@ class Example1: UIViewController, TableViewManagerCompatible {
default:
self.transition(to: .all)
}
- item.deselect(in: self.vc.tableViewManager, animated: true)
+ item.deselect(animated: true)
}
return item
}
diff --git a/Examples/SampleApp/SampleApp/PickerControl.swift b/Examples/SampleApp/SampleApp/PickerControl.swift
index 85fa1a2..8c7f793 100644
--- a/Examples/SampleApp/SampleApp/PickerControl.swift
+++ b/Examples/SampleApp/SampleApp/PickerControl.swift
@@ -145,9 +145,9 @@ open class PickerControl: NSObject {
}
}
- if components.count != 0 {
+ if components.isEmpty {
self.components.append(components)
- self.selections = Array.init(repeating: nil, count: self.components.count)
+ self.selections = Array(repeating: nil, count: self.components.count)
}
} else {
let item = PickerItem(title: String(describing: value), value: value)
@@ -323,7 +323,7 @@ open class PickerControl: NSObject {
if type == .single {
- if items.count == 0 {
+ if items.isEmpty {
return
}
@@ -332,7 +332,7 @@ open class PickerControl: NSObject {
selection = item
} else if type == .multiColumn {
- if components.count == 0 {
+ if components.isEmpty {
return
}
diff --git a/Examples/SampleApp/SampleApp/SelectionViewController.swift b/Examples/SampleApp/SampleApp/SelectionViewController.swift
index d7302b8..e6b21f0 100644
--- a/Examples/SampleApp/SampleApp/SelectionViewController.swift
+++ b/Examples/SampleApp/SampleApp/SelectionViewController.swift
@@ -125,13 +125,13 @@ public class SelectionViewController: UITableViewController {
if let checkedItem = itemSelected() {
checkedItem.selected = false
checkedItem.accessoryType = .none
- checkedItem.reload(in: tableViewManager, with: .fade)
+ checkedItem.reload(with: .fade)
}
}
item.selected = !item.selected
item.accessoryType = item.accessoryType == .checkmark ? .none : .checkmark
- item.reload(in: tableViewManager, with: .fade)
+ item.reload(with: .fade)
fillSelected()
}
diff --git a/Examples/SampleApp/SampleApp/Validator/Validation.swift b/Examples/SampleApp/SampleApp/Validator/Validation.swift
index a861f4c..b860af8 100644
--- a/Examples/SampleApp/SampleApp/Validator/Validation.swift
+++ b/Examples/SampleApp/SampleApp/Validator/Validation.swift
@@ -44,10 +44,10 @@ open class Validation {
public init(forInput: @escaping () -> Input, withIdentifier identifier: Any? = nil, rule: R) where R.Input == Input {
self.forInput = forInput
self.identifier = identifier
- self.rules.append(AnyValidatable.init(base: rule))
+ self.rules.append(AnyValidatable(base: rule))
}
open func add(rule: R) where R.Input == Input {
- self.rules.append(AnyValidatable.init(base: rule))
+ self.rules.append(AnyValidatable(base: rule))
}
}
diff --git a/Examples/SampleApp/SampleApp/Validator/Validator.swift b/Examples/SampleApp/SampleApp/Validator/Validator.swift
index 34a592e..31cc14c 100644
--- a/Examples/SampleApp/SampleApp/Validator/Validator.swift
+++ b/Examples/SampleApp/SampleApp/Validator/Validator.swift
@@ -36,7 +36,7 @@ public struct ValidatorManager {
internal var validations: [Validation] = []
public mutating func add(_ getInput: @escaping () -> Input, withRule rule: R) where R.Input == Input {
- validations.append(Validation.init(forInput: getInput, rule: rule))
+ validations.append(Validation(forInput: getInput, rule: rule))
}
public mutating func add(validation: Validation) {
diff --git a/Examples/SampleApp/SampleApp/ValidatorTests.swift b/Examples/SampleApp/SampleApp/ValidatorTests.swift
index 3a0f436..3887465 100644
--- a/Examples/SampleApp/SampleApp/ValidatorTests.swift
+++ b/Examples/SampleApp/SampleApp/ValidatorTests.swift
@@ -40,7 +40,7 @@ class ValidatorTests: XCTestCase {
var validator = ValidatorManager()
validator.add(validation: validation)
- expect(validator.errors.count).to(equal(0))
+ expect(validator.errors.count) == 0
}
func testCharactersLengthRule() {
@@ -53,7 +53,7 @@ class ValidatorTests: XCTestCase {
var validator = ValidatorManager()
validator.add(validation: validation)
- expect(validator.errors.count).to(equal(0))
+ expect(validator.errors.count) == 0
}
func testNumberBetweenRule() {
@@ -66,6 +66,6 @@ class ValidatorTests: XCTestCase {
var validator = ValidatorManager()
validator.add(validation: validation)
- expect(validator.errors.count).to(equal(0))
+ expect(validator.errors.count) == 0
}
}
diff --git a/Examples/SampleApp/SampleApp/ViewController.swift b/Examples/SampleApp/SampleApp/ViewController.swift
index 49dba19..e881727 100644
--- a/Examples/SampleApp/SampleApp/ViewController.swift
+++ b/Examples/SampleApp/SampleApp/ViewController.swift
@@ -16,12 +16,12 @@ class ViewController: UIViewController, TableViewManagerCompatible {
let mappedItems = array.map({ (className) -> Item in
let viewController = className.init(nibName: String(describing: className), bundle: nil)
- let navigationController = UINavigationController.init(rootViewController: viewController)
+ let navigationController = UINavigationController(rootViewController: viewController)
let item = CustomItem(title: "Example 1")
item.onSelection = { _ in
self.vc.present(navigationController, animated: true, completion: {
- item.deselect(in: self.vc.tableViewManager, animated: false)
+ item.deselect(animated: false)
})
}
return item
diff --git a/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutItem.swift b/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutItem.swift
index c6e6e8f..59683dd 100644
--- a/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutItem.swift
+++ b/Examples/Viper/TableViewKit+VIPER/AboutModule/MoreAboutItem.swift
@@ -58,8 +58,6 @@ class MoreAboutItem: Item, Selectable, Editable {
presenter?.showRateApp()
}
- if let manager = manager {
- deselect(in: manager, animated: true)
- }
+ deselect(animated: true)
}
}
diff --git a/External/Nimble b/External/Nimble
index 8e2703f..39b6700 160000
--- a/External/Nimble
+++ b/External/Nimble
@@ -1 +1 @@
-Subproject commit 8e2703fabde578a6d28537c73844f84d48e71200
+Subproject commit 39b67002306fda9de4c9fd1290a6295f97edd09e
diff --git a/README.md b/README.md
index 2417e8b..78530f3 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-Empowering `UITableView` with painless multi-type cell support and build-in automatic state transition animations
+Empowering `UITableView` with painless multi-type cell support and built-in automatic state transition animations
## Getting Started
- [Download TableViewKit](https://github.com/odigeoteam/TableViewKit/releases) and play with our [examples](https://github.com/odigeoteam/TableViewKit/tree/develop/Examples)
diff --git a/TableViewKit.podspec b/TableViewKit.podspec
index 9fa19de..e5e8849 100644
--- a/TableViewKit.podspec
+++ b/TableViewKit.podspec
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|
s.name = "TableViewKit"
- s.version = "1.0.0"
- s.summary = "Empowering UITableView with painless multi-type cell support and build-in automatic state transition animations"
+ s.version = "1.1.0"
+ s.summary = "Empowering UITableView with painless multi-type cell support and built-in automatic state transition animations"
s.homepage = "http://github.com/odigeoteam/TableViewKit/"
s.license = "MIT"
s.author = "TableViewKit Contributors"
diff --git a/TableViewKit.xcodeproj/project.pbxproj b/TableViewKit.xcodeproj/project.pbxproj
index 1750484..a9afc97 100644
--- a/TableViewKit.xcodeproj/project.pbxproj
+++ b/TableViewKit.xcodeproj/project.pbxproj
@@ -13,6 +13,8 @@
2564E61C1D88450F00A9DC3E /* TestRegisterHeaderFooterView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2564E61B1D88450F00A9DC3E /* TestRegisterHeaderFooterView.xib */; };
25837C781D87F819001EF4B8 /* ItemCompatible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25837C771D87F819001EF4B8 /* ItemCompatible.swift */; };
25B0B7541DC74F6C00591467 /* Editable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25B0B7531DC74F6C00591467 /* Editable.swift */; };
+ CC09F8891EA79178006FF4EA /* TableViewKitDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC09F8881EA79178006FF4EA /* TableViewKitDataSource.swift */; };
+ CC09F88B1EA791B7006FF4EA /* TableViewKitDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC09F88A1EA791B7006FF4EA /* TableViewKitDelegate.swift */; };
CC0DE2C81DE33B9C00E2EDE5 /* AnyEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC0DE2C71DE33B9C00E2EDE5 /* AnyEquatable.swift */; };
CC5CF11C1D839E71004DECB3 /* ArrayDiff.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5CF11B1D839E71004DECB3 /* ArrayDiff.swift */; };
CC97D76D1D741DC4009CDF9D /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC97D76C1D741DC4009CDF9D /* Selectable.swift */; };
@@ -55,6 +57,8 @@
25837C771D87F819001EF4B8 /* ItemCompatible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ItemCompatible.swift; path = Protocols/ItemCompatible.swift; sourceTree = ""; };
25B0B7531DC74F6C00591467 /* Editable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Editable.swift; path = Protocols/Editable.swift; sourceTree = ""; };
4BCAD51C1D89A704002F3420 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = "External/Nimble/build/Debug-iphoneos/Nimble.framework"; sourceTree = ""; };
+ CC09F8881EA79178006FF4EA /* TableViewKitDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewKitDataSource.swift; sourceTree = ""; };
+ CC09F88A1EA791B7006FF4EA /* TableViewKitDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewKitDelegate.swift; sourceTree = ""; };
CC0DE2C71DE33B9C00E2EDE5 /* AnyEquatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AnyEquatable.swift; path = Protocols/AnyEquatable.swift; sourceTree = ""; };
CC5CF11B1D839E71004DECB3 /* ArrayDiff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArrayDiff.swift; sourceTree = ""; };
CC97D76C1D741DC4009CDF9D /* Selectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Selectable.swift; path = Protocols/Selectable.swift; sourceTree = ""; };
@@ -155,6 +159,8 @@
CCCAC1271D7DA2CF0001FC1D /* Height.swift */,
CCEF387D1D8D59C000F5893F /* NibClassType.swift */,
CCBE458E1D72F79C00414A64 /* TableViewManager.swift */,
+ CC09F88A1EA791B7006FF4EA /* TableViewKitDelegate.swift */,
+ CC09F8881EA79178006FF4EA /* TableViewKitDataSource.swift */,
CCBE455F1D72F69500414A64 /* TableViewKit.h */,
CCBE45611D72F69500414A64 /* Info.plist */,
CC5CF11B1D839E71004DECB3 /* ArrayDiff.swift */,
@@ -205,6 +211,7 @@
buildConfigurationList = CCBE45701D72F69500414A64 /* Build configuration list for PBXNativeTarget "TableViewKit" */;
buildPhases = (
CCBE45571D72F69400414A64 /* Sources */,
+ CCC87B4A1ED9E7160008968D /* SwiftLint */,
CCBE45581D72F69400414A64 /* Frameworks */,
CCBE45591D72F69400414A64 /* Headers */,
CCBE455A1D72F69400414A64 /* Resources */,
@@ -293,12 +300,30 @@
};
/* End PBXResourcesBuildPhase section */
+/* Begin PBXShellScriptBuildPhase section */
+ CCC87B4A1ED9E7160008968D /* SwiftLint */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = SwiftLint;
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi";
+ };
+/* End PBXShellScriptBuildPhase section */
+
/* Begin PBXSourcesBuildPhase section */
CCBE45571D72F69400414A64 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CCBE45A01D72F79C00414A64 /* Section.swift in Sources */,
+ CC09F88B1EA791B7006FF4EA /* TableViewKitDelegate.swift in Sources */,
CC0DE2C81DE33B9C00E2EDE5 /* AnyEquatable.swift in Sources */,
CCFD05CC1D95BD3C0063A002 /* Stateful.swift in Sources */,
2519E1C91E013DF00021E06E /* UITableView+Moves.swift in Sources */,
@@ -313,6 +338,7 @@
CCDB8FAB1D83649B00E44A99 /* ObservableArray.swift in Sources */,
CCCAC1261D7D9EE00001FC1D /* HeaderFooter.swift in Sources */,
CCBE45A11D72F79C00414A64 /* TableViewManager.swift in Sources */,
+ CC09F8891EA79178006FF4EA /* TableViewKitDataSource.swift in Sources */,
CCBE45931D72F79C00414A64 /* UITableView+Register.swift in Sources */,
CCBE459D1D72F79C00414A64 /* CellDrawer.swift in Sources */,
CCBE459F1D72F79C00414A64 /* Item.swift in Sources */,
diff --git a/TableViewKit/ArrayDiff.swift b/TableViewKit/ArrayDiff.swift
index 9466d99..85ba89e 100644
--- a/TableViewKit/ArrayDiff.swift
+++ b/TableViewKit/ArrayDiff.swift
@@ -1,20 +1,34 @@
import Foundation
-struct Diff {
+struct Diff {
var inserts: [Int]
var deletes: [Int]
var moves: [(Int, Int)]
+ var insertsElement: [Element]
+ var deletesElement: [Element]
+
var isEmpty: Bool {
return (inserts.count + deletes.count + moves.count) == 0
}
- init(inserts: [Int] = [], deletes: [Int] = [], moves: [(Int, Int)] = []) {
+ init(inserts: [Int] = [], deletes: [Int] = [], moves: [(Int, Int)] = [],
+ insertsElement: [Element] = [], deletesElement: [Element] = []) {
self.inserts = inserts
self.deletes = deletes
self.moves = moves
+ self.insertsElement = insertsElement
+ self.deletesElement = deletesElement
+ }
}
+enum ArrayIndexesChanges {
+ case inserts(Int)
+ case deletes(Int)
+ case updates(Int)
+ case moves(Int, Int)
+ case beginUpdates
+ case endUpdates
}
class DiffIterator: IteratorProtocol {
@@ -27,23 +41,23 @@ class DiffIterator: IteratorProtocol {
init(matrix: Matrix) {
self.matrix = matrix
- self.last = Coordinates(x: matrix.rows-1, y: matrix.columns-1)
+ self.last = Coordinates(x: matrix.rows - 1, y: matrix.columns - 1)
}
- func next() -> ArrayChanges? {
- while(last.x > 0 || last.y > 0) {
+ func next() -> ArrayIndexesChanges? {
+ while last.x > 0 || last.y > 0 {
if last.x == 0 {
last.y -= 1
- return .inserts([last.y])
+ return .inserts(last.y)
} else if last.y == 0 {
last.x -= 1
- return .deletes([last.x])
+ return .deletes(last.x)
} else if matrix[last.x, last.y] == matrix[last.x, last.y - 1] {
last.y -= 1
- return .inserts([last.y])
+ return .inserts(last.y)
} else if matrix[last.x, last.y] == matrix[last.x - 1, last.y] {
last.x -= 1
- return .deletes([last.x])
+ return .deletes(last.x)
} else {
last.x -= 1
last.y -= 1
@@ -91,29 +105,29 @@ extension Array {
typealias Predicate = (Element, Element) -> Bool
- static func diff(between x: [Element], and y: [Element], where predicate: Predicate) -> Diff {
+ static func diff(between x: [Element], and y: [Element], where predicate: Predicate) -> Diff {
- var matrix = Matrix(rows: x.count+1, columns: y.count+1, repeatedValue: 0)
+ var matrix = Matrix(rows: x.count + 1, columns: y.count + 1, repeatedValue: 0)
for (i, xElem) in x.enumerated() {
for (j, yElem) in y.enumerated() {
if predicate(xElem, yElem) {
- matrix[i+1, j+1] = matrix[i, j] + 1
+ matrix[i + 1, j + 1] = matrix[i, j] + 1
} else {
- matrix[i+1, j+1] = Swift.max(matrix[i, j+1], matrix[i+1, j])
+ matrix[i + 1, j + 1] = Swift.max(matrix[i, j + 1], matrix[i + 1, j])
}
}
}
- let changes = [ArrayChanges](DiffSequence(matrix: matrix))
- var inserts: [Int] = changes.flatMap { change -> [Int] in
- guard case .inserts(let array) = change else { return [] }
- return array
- }.sorted { $0 > $1 }
+ let changes = [ArrayIndexesChanges](DiffSequence(matrix: matrix))
+ var inserts: [Int] = changes.flatMap { change -> Int? in
+ guard case .inserts(let index) = change else { return nil }
+ return index
+ }.sorted { $0 > $1 }
- var deletes: [Int] = changes.flatMap { change -> [Int] in
- guard case .deletes(let array) = change else { return [] }
- return array
- }.sorted { $0 < $1 }
+ var deletes: [Int] = changes.flatMap { change -> Int? in
+ guard case .deletes(let index) = change else { return nil }
+ return index
+ }.sorted { $0 < $1 }
var moves: [(Int, Int)] = []
@@ -131,7 +145,13 @@ extension Array {
deleted += 1
}
- return Diff(inserts: inserts, deletes: deletes, moves: moves)
+ let diff = Diff(inserts: inserts,
+ deletes: deletes,
+ moves: moves,
+ insertsElement: inserts.flatMap { y[$0] },
+ deletesElement: deletes.flatMap { x[$0] })
+
+ return diff
}
}
diff --git a/TableViewKit/Extensions/NSIndexSet+Array.swift b/TableViewKit/Extensions/NSIndexSet+Array.swift
index 3fe5d88..50f8ccc 100644
--- a/TableViewKit/Extensions/NSIndexSet+Array.swift
+++ b/TableViewKit/Extensions/NSIndexSet+Array.swift
@@ -7,7 +7,7 @@ extension IndexSet {
/// - parameter array: An array of integers
init(_ array: [Int]) {
let mutable = NSMutableIndexSet()
- array.forEach {mutable.add($0)}
+ array.forEach { mutable.add($0) }
self.init(mutable)
}
}
diff --git a/TableViewKit/Extensions/UITableView+Moves.swift b/TableViewKit/Extensions/UITableView+Moves.swift
index b1677e3..25c5997 100644
--- a/TableViewKit/Extensions/UITableView+Moves.swift
+++ b/TableViewKit/Extensions/UITableView+Moves.swift
@@ -4,13 +4,13 @@ import UIKit
extension UITableView {
func moveRows(at indexPaths: [IndexPath], to newIndexPaths: [IndexPath]) {
- for (index, _) in indexPaths.enumerated() {
+ indexPaths.indices.forEach { index in
moveRow(at: indexPaths[index], to: newIndexPaths[index])
}
}
func moveSections(from: [Int], to: [Int]) {
- for (index, _) in from.enumerated() {
+ from.indices.forEach { index in
moveSection(from[index], toSection: to[index])
}
}
diff --git a/TableViewKit/Height.swift b/TableViewKit/Height.swift
index c6577f9..442f70a 100644
--- a/TableViewKit/Height.swift
+++ b/TableViewKit/Height.swift
@@ -24,7 +24,7 @@ public enum Height {
switch self {
case .static(let value):
return value
- case .dynamic(_):
+ case .dynamic:
return UITableViewAutomaticDimension
}
}
diff --git a/TableViewKit/ObservableArray.swift b/TableViewKit/ObservableArray.swift
index 6352ec0..930dc50 100644
--- a/TableViewKit/ObservableArray.swift
+++ b/TableViewKit/ObservableArray.swift
@@ -1,21 +1,20 @@
import Foundation
-enum ArrayChanges {
- case inserts([Int])
- case deletes([Int])
+enum ArrayChanges {
+ case inserts([Int], [Element])
+ case deletes([Int], [Element])
case updates([Int])
case moves([(Int, Int)])
case beginUpdates
case endUpdates
}
-
/// An observable array. It will notify any kind of changes.
public struct ObservableArray: ExpressibleByArrayLiteral, Collection, MutableCollection, RangeReplaceableCollection {
/// The type of the elements of an array literal.
public typealias Element = T
- private var diff = Diff()
+ private var diff: Diff!
private var array: [T] {
willSet {
@@ -23,14 +22,15 @@ public struct ObservableArray: ExpressibleByArrayLiteral, Collection, Mutable
}
didSet {
callback?(.beginUpdates)
- if (!diff.moves.isEmpty) { callback?(.moves(diff.moves)) }
- if (!diff.deletes.isEmpty) { callback?(.deletes(diff.deletes)) }
- if (!diff.inserts.isEmpty) { callback?(.inserts(diff.inserts)) }
+ if !diff.moves.isEmpty { callback?(.moves(diff.moves)) }
+ if !diff.deletes.isEmpty { callback?(.deletes(diff.deletes, diff.deletesElement)) }
+ if !diff.inserts.isEmpty { callback?(.inserts(diff.inserts, diff.insertsElement)) }
callback?(.endUpdates)
+ diff = nil
}
}
- var callback: ((ArrayChanges) -> Void)?
+ var callback: ((ArrayChanges) -> Void)?
/// Creates an empty `ObservableArray`
public init() {
@@ -71,8 +71,8 @@ public struct ObservableArray: ExpressibleByArrayLiteral, Collection, Mutable
/// - parameter i: A valid index of the collection. i must be less than endIndex.
///
/// - returns: The index value immediately after i.
- public func index(after i: Int) -> Int {
- return array.index(after: i)
+ public func index(after index: Int) -> Int {
+ return array.index(after: index)
}
/// A Boolean value indicating whether the collection is empty.
@@ -101,6 +101,7 @@ public struct ObservableArray: ExpressibleByArrayLiteral, Collection, Mutable
///
/// - parameter subrange: The subrange that must be replaced
/// - parameter newElements: The new elements that must be replaced
+ // swiftlint:disable:next line_length
public mutating func replaceSubrange(_ subrange: Range, with newElements: C) where C : Collection, C.Iterator.Element == T {
array.replaceSubrange(subrange, with: newElements)
}
diff --git a/TableViewKit/Protocols/CellDrawer.swift b/TableViewKit/Protocols/CellDrawer.swift
index 3b7593a..20d9f63 100644
--- a/TableViewKit/Protocols/CellDrawer.swift
+++ b/TableViewKit/Protocols/CellDrawer.swift
@@ -41,6 +41,7 @@ public extension CellDrawer {
cell.item = item as? Item
}
+ // swiftlint:disable:next force_cast
return cell as! Cell
}
}
@@ -52,9 +53,12 @@ public struct AnyCellDrawer {
let draw: (UITableViewCell, Item) -> Void
/// Creates a type-erased drawer that wraps the given cell drawer
- public init(_ drawer: Drawer.Type) where Drawer.GenericItem == GenericItem, Drawer.Cell == Cell {
+ public init(_ drawer: Drawer.Type)
+ where Drawer.GenericItem == GenericItem, Drawer.Cell == Cell {
self.type = drawer.type.cellType
+ // swiftlint:disable:next force_cast
self.cell = { manager, item, indexPath in drawer.cell(in: manager, with: item as! GenericItem, for: indexPath) }
+ // swiftlint:disable:next force_cast
self.draw = { cell, item in drawer.draw(cell as! Cell, with: item as! GenericItem) }
}
diff --git a/TableViewKit/Protocols/HeaderFooterDrawer.swift b/TableViewKit/Protocols/HeaderFooterDrawer.swift
index 8b5be49..9703065 100644
--- a/TableViewKit/Protocols/HeaderFooterDrawer.swift
+++ b/TableViewKit/Protocols/HeaderFooterDrawer.swift
@@ -31,6 +31,7 @@ public extension HeaderFooterDrawer {
/// Returns a dequeued header/footer
static func view(in manager: TableViewManager, with item: GenericItem) -> View {
+ // swiftlint:disable:next force_cast
return manager.tableView.dequeueReusableHeaderFooterView(withIdentifier: self.type.reusableIdentifier) as! View
}
}
@@ -42,9 +43,12 @@ public struct AnyHeaderFooterDrawer {
let draw: (UITableViewHeaderFooterView, HeaderFooter) -> Void
/// Creates a type-erased drawer that wraps the given header or footer drawer
- public init(_ drawer: Drawer.Type) where Drawer.GenericItem == GenericItem, Drawer.View == View {
+ public init(_ drawer: Drawer.Type)
+ where Drawer.GenericItem == GenericItem, Drawer.View == View {
self.type = drawer.type.headerFooterType
+ // swiftlint:disable:next force_cast
self.view = { manager, item in drawer.view(in: manager, with: item as! GenericItem) }
+ // swiftlint:disable:next force_cast
self.draw = { cell, item in drawer.draw(cell as! View, with: item as! GenericItem) }
}
diff --git a/TableViewKit/Protocols/Item.swift b/TableViewKit/Protocols/Item.swift
index 48f4b87..3254205 100644
--- a/TableViewKit/Protocols/Item.swift
+++ b/TableViewKit/Protocols/Item.swift
@@ -20,6 +20,24 @@ public extension Item where Self: Equatable {
}
}
+// swiftlint:disable:next identifier_name
+private var ItemTableViewManagerKey: UInt8 = 0
+
+extension Item {
+
+ public internal(set) var manager: TableViewManager? {
+ get {
+ return objc_getAssociatedObject(self, &ItemTableViewManagerKey) as? TableViewManager
+ }
+ set(newValue) {
+ objc_setAssociatedObject(self,
+ &ItemTableViewManagerKey,
+ newValue as AnyObject,
+ objc_AssociationPolicy.OBJC_ASSOCIATION_ASSIGN)
+ }
+ }
+}
+
extension Item {
public func equals(_ other: Any?) -> Bool {
@@ -34,41 +52,37 @@ extension Item {
return .dynamic(44.0)
}
- /// Returns the `section` of the `item` in the specified `manager`
- ///
- /// - parameter manager: A `manager` where the `item` may have been added
+ /// Returns the `section` of the `item`
///
/// - returns: The `section` of the `item` or `nil` if not present
- public func section(in manager: TableViewManager) -> Section? {
- guard let indexPath = self.indexPath(in: manager) else { return nil }
- return manager.sections[indexPath.section]
+ public var section: Section? {
+ guard let indexPath = indexPath else { return nil }
+ return manager?.sections[indexPath.section]
}
- /// Returns the `indexPath` of the `item` in the specified `manager`
- ///
- /// - parameter manager: A `manager` where the `item` may have been added
+ /// Returns the `indexPath` of the `item`
///
/// - returns: The `indexPath` of the `item` or `nil` if not present
- public func indexPath(in manager: TableViewManager) -> IndexPath? {
+ public var indexPath: IndexPath? {
+ guard let manager = manager else { return nil }
for section in manager.sections {
guard
- let sectionIndex = section.index(in: manager),
+ let sectionIndex = section.index,
let rowIndex = section.items.index(of: self) else { continue }
return IndexPath(row: rowIndex, section: sectionIndex)
}
return nil
}
- /// Reload the `item` in the specified `manager` with an `animation`
+ /// Reload the `item` with an `animation`
///
- /// - parameter manager: A `manager` where the `item` may have been added
/// - parameter animation: A constant that indicates how the reloading is to be animated
///
/// - returns: The `section` of the `item` or `nil` if not present
- public func reload(in manager: TableViewManager, with animation: UITableViewRowAnimation = .automatic) {
- guard let indexPath = self.indexPath(in: manager) else { return }
- let section = manager.sections[indexPath.section]
- section.items.callback?(.updates([indexPath.row]))
+ public func reload(with animation: UITableViewRowAnimation = .automatic) {
+ guard let indexPath = indexPath else { return }
+ let section = manager?.sections[indexPath.section]
+ section?.items.callback?(.updates([indexPath.row]))
}
}
diff --git a/TableViewKit/Protocols/Selectable.swift b/TableViewKit/Protocols/Selectable.swift
index 0d1b173..2da5d7d 100644
--- a/TableViewKit/Protocols/Selectable.swift
+++ b/TableViewKit/Protocols/Selectable.swift
@@ -11,22 +11,26 @@ extension Selectable {
/// Select the `item`
///
- /// - parameter manager: A `manager` where the `item` may have been added
/// - parameter animated: If the selection should be animated
/// - parameter scrollPosition: The scrolling position
- public func select(in manager: TableViewManager, animated: Bool, scrollPosition: UITableViewScrollPosition = .none) {
+ // swiftlint:disable:next line_length
+ public func select(animated: Bool, scrollPosition: UITableViewScrollPosition = .none) {
+ guard let tableView = manager?.tableView else { return }
- manager.tableView.selectRow(at: indexPath(in: manager), animated: animated, scrollPosition: scrollPosition)
- manager.tableView(manager.tableView, didSelectRowAt: indexPath(in: manager)!)
+ tableView.selectRow(at: indexPath, animated: animated, scrollPosition: scrollPosition)
+ if let indexPath = indexPath {
+ tableView.delegate?.tableView?(tableView, didSelectRowAt: indexPath)
+ }
}
/// Deselect the `item`
///
- /// - parameter manager: A `manager` where the `item` may have been added
/// - parameter animated: If the selection should be animated
- public func deselect(in manager: TableViewManager, animated: Bool) {
- if let indexPath = indexPath(in: manager) {
- manager.tableView.deselectRow(at: indexPath, animated: animated)
+ public func deselect(animated: Bool) {
+ guard let tableView = manager?.tableView else { return }
+
+ if let indexPath = indexPath {
+ tableView.deselectRow(at: indexPath, animated: animated)
}
}
diff --git a/TableViewKit/Section.swift b/TableViewKit/Section.swift
index 6af9e89..59e5945 100644
--- a/TableViewKit/Section.swift
+++ b/TableViewKit/Section.swift
@@ -14,7 +14,7 @@ public protocol Section: class, AnyEquatable {
/// The `footer` of the section, none if not defined
var footer: HeaderFooterView { get }
- func index(in manager: TableViewManager) -> Int?
+ var index: Int? { get }
}
public extension Section where Self: Equatable {
@@ -35,6 +35,24 @@ extension Section {
public var footer: HeaderFooterView { return nil }
}
+// swiftlint:disable:next identifier_name
+private var SectionTableViewManagerKey: UInt8 = 0
+
+extension Section {
+
+ public internal(set) var manager: TableViewManager? {
+ get {
+ return objc_getAssociatedObject(self, &SectionTableViewManagerKey) as? TableViewManager
+ }
+ set(newValue) {
+ objc_setAssociatedObject(self,
+ &SectionTableViewManagerKey,
+ newValue as AnyObject,
+ objc_AssociationPolicy.OBJC_ASSOCIATION_ASSIGN)
+ }
+ }
+}
+
extension Section {
public func equals(_ other: Any?) -> Bool {
@@ -44,55 +62,67 @@ extension Section {
return false
}
- /// Returns the `index` of the `section` in the specified `manager`
- ///
- /// - parameter manager: A `manager` where the `section` may have been added
+ /// Returns the `index` of the `section`
///
/// - returns: The `index` of the `section` or `nil` if not present
- public func index(in manager: TableViewManager) -> Int? {
- return manager.sections.index(of: self)
+ public var index: Int? {
+ return manager?.sections.index(of: self)
}
/// Register the section in the specified manager
///
/// - parameter manager: A manager where the section may have been added
internal func register(in manager: TableViewManager) {
+ setup(in: manager)
+
if case .view(let header) = header {
- manager.tableView.register(type(of: header).drawer.type)
+ manager.register(type(of: header).drawer.type)
}
if case .view(let footer) = footer {
- manager.tableView.register(type(of: footer).drawer.type)
+ manager.register(type(of: footer).drawer.type)
}
- items.forEach {
- manager.tableView.register(type(of: $0).drawer.type)
+ items.forEach { item in
+ item.manager = manager
+ manager.register(type(of: item).drawer.type)
}
}
+ /// Unregister the section
+ internal func unregister() {
+ self.manager = nil
+ items.forEach { $0.manager = nil }
+ }
+
/// Setup the section internals
///
/// - parameter manager: A manager where the section may have been added
- internal func setup(in manager: TableViewManager) {
- items.callback = { [weak self, weak manager] change in
- if let manager = manager {
- self?.onItemsUpdate(withChanges: change, in: manager)
+ private func setup(in manager: TableViewManager) {
+ self.manager = manager
+
+ items.callback = { [weak self] change in
+ if let weakSelf = self, let manager = weakSelf.manager {
+ weakSelf.onItemsUpdate(withChanges: change, in: manager)
}
}
}
- private func onItemsUpdate(withChanges changes: ArrayChanges, in manager: TableViewManager) {
+ private func onItemsUpdate(withChanges changes: ArrayChanges- , in manager: TableViewManager) {
- guard let sectionIndex = index(in: manager) else { return }
+ guard let sectionIndex = index else { return }
let tableView = manager.tableView
- if case .inserts(let array) = changes {
- array.forEach { manager.register(type(of: items[$0]).drawer.type) }
- }
+ if case .inserts(_, let items) = changes {
+ items.forEach { item in
+ item.manager = manager
+ manager.register(type(of: item).drawer.type)
+ }
+ }
switch changes {
- case .inserts(let array):
- let indexPaths = array.map { IndexPath(item: $0, section: sectionIndex) }
+ case .inserts(let array, _):
+ let indexPaths = array.map { IndexPath(item: $0, section: sectionIndex) }
tableView.insertRows(at: indexPaths, with: manager.animation)
- case .deletes(let array):
+ case .deletes(let array, _):
let indexPaths = array.map { IndexPath(item: $0, section: sectionIndex) }
tableView.deleteRows(at: indexPaths, with: manager.animation)
case .updates(let array):
diff --git a/TableViewKit/TableViewKitDataSource.swift b/TableViewKit/TableViewKitDataSource.swift
new file mode 100644
index 0000000..0f0da17
--- /dev/null
+++ b/TableViewKit/TableViewKitDataSource.swift
@@ -0,0 +1,62 @@
+import UIKit
+
+open class TableViewKitDataSource: NSObject, UITableViewDataSource {
+
+ open unowned var manager: TableViewManager
+ private var sections: ObservableArray { return manager.sections }
+
+ public required init(manager: TableViewManager) {
+ self.manager = manager
+ }
+
+ /// Implementation of UITableViewDataSource
+ open func numberOfSections(in tableView: UITableView) -> Int {
+ return sections.count
+ }
+
+ /// Implementation of UITableViewDataSource
+ open func tableView(_ tableView: UITableView, numberOfRowsInSection sectionIndex: Int) -> Int {
+ let section = sections[sectionIndex]
+ return section.items.count
+ }
+
+ /// Implementation of UITableViewDataSource
+ open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+ let currentItem = manager.item(at: indexPath)
+ let drawer = type(of: currentItem).drawer
+
+ let cell = drawer.cell(in: manager, with: currentItem, for: indexPath)
+ drawer.draw(cell, with: currentItem)
+
+ return cell
+ }
+
+ /// Implementation of UITableViewDataSource
+ open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
+ return title(for: { $0.header }, inSection: section)
+ }
+
+ /// Implementation of UITableViewDataSource
+ open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
+ return title(for: { $0.footer }, inSection: section)
+ }
+
+ /// Implementation of UITableViewDataSource
+ // swiftlint:disable:next line_length
+ open func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
+ // Intentionally blank. Required to use UITableViewRowActions
+ }
+
+ /// Implementation of UITableViewDataSource
+ open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
+ return manager.item(at: indexPath) is Editable
+ }
+
+ fileprivate func title(for key: (Section) -> HeaderFooterView, inSection section: Int) -> String? {
+ if case .title(let value) = key(sections[section]) {
+ return value
+ }
+ return nil
+ }
+
+}
diff --git a/TableViewKit/TableViewKitDelegate.swift b/TableViewKit/TableViewKitDelegate.swift
new file mode 100644
index 0000000..e526672
--- /dev/null
+++ b/TableViewKit/TableViewKitDelegate.swift
@@ -0,0 +1,119 @@
+import UIKit
+
+open class TableViewKitDelegate: NSObject, UITableViewDelegate {
+
+ open unowned var manager: TableViewManager
+ open weak var scrollDelegate: UIScrollViewDelegate?
+ private var sections: ObservableArray { return manager.sections }
+
+ public required init(manager: TableViewManager) {
+ self.manager = manager
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+ guard let currentItem = manager.item(at: indexPath) as? Selectable else { return }
+ currentItem.didSelect()
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+ return height(at: indexPath) ?? tableView.rowHeight
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
+ return height(for: { $0.header }, inSection: section) ?? tableView.sectionHeaderHeight
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
+ return height(for: { $0.footer }, inSection: section) ?? tableView.sectionFooterHeight
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
+ return estimatedHeight(at: indexPath) ?? tableView.estimatedRowHeight
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
+ return estimatedHeight(for: { $0.header }, inSection: section) ?? tableView.estimatedSectionHeaderHeight
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat {
+ return estimatedHeight(for: { $0.footer }, inSection: section) ?? tableView.estimatedSectionHeaderHeight
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
+ return view(for: { $0.header }, inSection: section)
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
+ return view(for: { $0.footer }, inSection: section)
+ }
+
+ /// Implementation of UITableViewDelegate
+ open func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
+ guard let item = manager.item(at: indexPath) as? Editable else { return nil }
+ return item.actions
+ }
+
+ /// Implementation of UIScrollViewDelegate
+ open func scrollViewDidScroll(_ scrollView: UIScrollView) {
+ scrollDelegate?.scrollViewDidScroll?(scrollView)
+ }
+
+ /// Implementation of UIScrollViewDelegate
+ open func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+ scrollDelegate?.scrollViewWillBeginDragging?(scrollView)
+ }
+
+ /// Implementation of UIScrollViewDelegate
+ open func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
+ scrollDelegate?.scrollViewDidEndDragging?(scrollView, willDecelerate: decelerate)
+ }
+
+ fileprivate func view(for key: (Section) -> HeaderFooterView, inSection section: Int) -> UIView? {
+ guard case .view(let item) = key(sections[section]) else { return nil }
+
+ let drawer = type(of: item).drawer
+ let view = drawer.view(in: manager, with: item)
+ drawer.draw(view, with: item)
+
+ return view
+ }
+
+ fileprivate func estimatedHeight(for key: (Section) -> HeaderFooterView, inSection section: Int) -> CGFloat? {
+ let item = key(sections[section])
+ switch item {
+ case .view(let view):
+ guard let height = view.height else { return nil }
+ return height.estimated
+ case .title:
+ return 1.0
+ default:
+ return nil
+ }
+ }
+
+ fileprivate func estimatedHeight(at indexPath: IndexPath) -> CGFloat? {
+ guard let height = manager.item(at: indexPath).height else { return nil }
+ return height.estimated
+ }
+
+ fileprivate func height(for key: (Section) -> HeaderFooterView, inSection section: Int) -> CGFloat? {
+ guard case .view(let view) = key(sections[section]), let value = view.height
+ else { return nil }
+ return value.height
+ }
+
+ fileprivate func height(at indexPath: IndexPath) -> CGFloat? {
+ guard let value = manager.item(at: indexPath).height else { return nil }
+ return value.height
+ }
+
+}
diff --git a/TableViewKit/TableViewManager.swift b/TableViewKit/TableViewManager.swift
index dc004f2..cd96384 100644
--- a/TableViewKit/TableViewManager.swift
+++ b/TableViewKit/TableViewManager.swift
@@ -5,7 +5,7 @@ import UIKit
/// It controls how to display an array of sections, from the header, to its items, to the footer.
/// It automatically registers any related initial or new views/cells for reusability.
/// Any changes of the sections will be automatically animated and reflected.
-open class TableViewManager: NSObject {
+open class TableViewManager {
/// The `tableView` linked to this manager
open let tableView: UITableView
@@ -15,66 +15,55 @@ open class TableViewManager: NSObject {
open var animation: UITableViewRowAnimation = .automatic
- var reusableIdentifiers: Set = []
+ open var dataSource: TableViewKitDataSource? { didSet { tableView.dataSource = dataSource } }
+ open var delegate: TableViewKitDelegate? { didSet { tableView.delegate = delegate } }
+ open var scrollDelegate: UIScrollViewDelegate? { didSet { delegate?.scrollDelegate = scrollDelegate } }
- /// Initialize a `TableViewManager` with a `tableView`.
- ///
- /// - parameter tableView: A `tableView` that will be controlled by the `TableViewManager`
- public init(tableView: UITableView) {
- self.tableView = tableView
- self.sections = []
- super.init()
- self.tableView.dataSource = self
- self.tableView.delegate = self
- self.setupSections()
-
- }
+ var reusableIdentifiers: Set = []
/// Initialize a `TableViewManager` with a `tableView` and an initial array of sections
///
/// - parameter tableView: A `tableView` that will be controlled by the `TableViewManager`
/// - parameter sections: An array of sections
- public init(tableView: UITableView, sections: [Section]) {
+ public init(tableView: UITableView, sections: [Section] = []) {
self.tableView = tableView
self.sections = ObservableArray(array: sections)
- super.init()
- self.tableView.dataSource = self
- self.tableView.delegate = self
+ self.setupDelegates()
self.setupSections()
}
+ private func setupDelegates() {
+ self.delegate = TableViewKitDelegate(manager: self)
+ self.dataSource = TableViewKitDataSource(manager: self)
+ }
+
private func setupSections() {
- sections.forEach { section in
- section.setup(in: self)
- section.register(in: self)
- }
+ sections.forEach { $0.register(in: self) }
sections.callback = { [weak self] in self?.onSectionsUpdate(withChanges: $0) }
}
- private func onSectionsUpdate(withChanges changes: ArrayChanges) {
+ private func onSectionsUpdate(withChanges changes: ArrayChanges) {
switch changes {
- case .inserts(let array):
- array.forEach { index in
- sections[index].setup(in: self)
- sections[index].register(in: self)
- }
- tableView.insertSections(IndexSet(array), with: animation)
- case .deletes(let array):
- tableView.deleteSections(IndexSet(array), with: animation)
- case .updates(let array):
- tableView.reloadSections(IndexSet(array), with: animation)
- case .moves(let array):
- let fromIndex = array.map { $0.0 }
- let toIndex = array.map { $0.1 }
+ case .inserts(let indexes, let insertedSections):
+ insertedSections.forEach { $0.register(in: self) }
+ tableView.insertSections(IndexSet(indexes), with: animation)
+ case .deletes(let indexes, let removedSections):
+ removedSections.forEach { $0.unregister() }
+ tableView.deleteSections(IndexSet(indexes), with: animation)
+ case .updates(let indexes):
+ tableView.reloadSections(IndexSet(indexes), with: animation)
+ case .moves(let indexes):
+ let fromIndex = indexes.map { $0.0 }
+ let toIndex = indexes.map { $0.1 }
tableView.moveSections(from: fromIndex, to: toIndex)
case .beginUpdates:
- if (animation == .none) {
+ if animation == .none {
UIView.setAnimationsEnabled(false)
}
tableView.beginUpdates()
case .endUpdates:
tableView.endUpdates()
- if (animation == .none) {
+ if animation == .none {
UIView.setAnimationsEnabled(true)
}
}
@@ -96,158 +85,8 @@ extension TableViewManager {
reusableIdentifiers.insert(type.reusableIdentifier)
}
}
-}
-extension TableViewManager {
-
- fileprivate func item(at indexPath: IndexPath) -> Item {
+ func item(at indexPath: IndexPath) -> Item {
return sections[indexPath.section].items[indexPath.row]
}
-
- fileprivate func view(for key: (Section) -> HeaderFooterView, inSection section: Int) -> UIView? {
- guard case .view(let item) = key(sections[section]) else { return nil }
-
- let drawer = type(of: item).drawer
- let view = drawer.view(in: self, with: item)
- drawer.draw(view, with: item)
-
- return view
- }
-
- fileprivate func title(for key: (Section) -> HeaderFooterView, inSection section: Int) -> String? {
- if case .title(let value) = key(sections[section]) {
- return value
- }
- return nil
-
- }
-
- fileprivate func estimatedHeight(for key: (Section) -> HeaderFooterView, inSection section: Int) -> CGFloat? {
- let item = key(sections[section])
- switch item {
- case .view(let view):
- guard let height = view.height else { return nil }
- return height.estimated
- case .title(_):
- return 1.0
- default:
- return nil
- }
- }
-
- fileprivate func estimatedHeight(at indexPath: IndexPath) -> CGFloat? {
- guard let height = item(at: indexPath).height else { return nil }
- return height.estimated
- }
-
- fileprivate func height(for key: (Section) -> HeaderFooterView, inSection section: Int) -> CGFloat? {
- guard case .view(let view) = key(sections[section]), let value = view.height
- else { return nil }
- return value.height
- }
-
- fileprivate func height(at indexPath: IndexPath) -> CGFloat? {
- guard let value = item(at: indexPath).height else { return nil }
- return value.height
- }
-
-}
-
-extension TableViewManager: UITableViewDataSource {
-
- /// Implementation of UITableViewDataSource
- open func numberOfSections(in tableView: UITableView) -> Int {
- return sections.count
- }
-
- /// Implementation of UITableViewDataSource
- open func tableView(_ tableView: UITableView, numberOfRowsInSection sectionIndex: Int) -> Int {
- let section = sections[sectionIndex]
- return section.items.count
- }
-
- /// Implementation of UITableViewDataSource
- open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let currentItem = item(at: indexPath)
- let drawer = type(of: currentItem).drawer
-
- let cell = drawer.cell(in: self, with: currentItem, for: indexPath)
- drawer.draw(cell, with: currentItem)
-
- return cell
- }
-
- /// Implementation of UITableViewDataSource
- open func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
- return title(for: {$0.header}, inSection: section)
- }
-
- /// Implementation of UITableViewDataSource
- open func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
- return title(for: {$0.footer}, inSection: section)
- }
-
- /// Implementation of UITableViewDataSource
- open func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
- // Intentionally blank. Required to use UITableViewRowActions
- }
-}
-
-extension TableViewManager: UITableViewDelegate {
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
- guard let currentItem = item(at: indexPath) as? Selectable else { return }
- currentItem.didSelect()
- }
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
- return height(at: indexPath) ?? tableView.rowHeight
- }
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
- return height(for: {$0.header}, inSection: section) ?? tableView.sectionHeaderHeight
- }
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
- return height(for: {$0.footer}, inSection: section) ?? tableView.sectionFooterHeight
- }
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
- return estimatedHeight(at: indexPath) ?? tableView.estimatedRowHeight
- }
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
- return estimatedHeight(for: {$0.header}, inSection: section) ?? tableView.estimatedSectionHeaderHeight
- }
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat {
- return estimatedHeight(for: {$0.footer}, inSection: section) ?? tableView.estimatedSectionHeaderHeight
- }
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
- return view(for: {$0.header}, inSection: section)
- }
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
- return view(for: {$0.footer}, inSection: section)
- }
-
- open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
- return item(at: indexPath) is Editable
- }
-
- /// Implementation of UITableViewDelegate
- open func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
- guard let item = item(at: indexPath) as? Editable else { return nil }
- return item.actions
- }
}
diff --git a/TableViewKitTests/TableViewDataSourceTests.swift b/TableViewKitTests/TableViewDataSourceTests.swift
index 895072c..a918251 100644
--- a/TableViewKitTests/TableViewDataSourceTests.swift
+++ b/TableViewKitTests/TableViewDataSourceTests.swift
@@ -63,6 +63,7 @@ class DifferentCell: UITableViewCell { }
class TableViewDataSourceTests: XCTestCase {
fileprivate var tableViewManager: TableViewManager!
+ fileprivate var dataSource: TableViewKitDataSource { return tableViewManager.dataSource! }
override func setUp() {
super.setUp()
@@ -80,7 +81,7 @@ class TableViewDataSourceTests: XCTestCase {
func testCellForRow() {
let indexPath = IndexPath(row: 0, section: 0)
- let cell = self.tableViewManager.tableView(self.tableViewManager.tableView, cellForRowAt: indexPath)
+ let cell = dataSource.tableView(self.tableViewManager.tableView, cellForRowAt: indexPath)
expect(cell).to(beAnInstanceOf(TestCell.self))
}
@@ -94,53 +95,40 @@ class TableViewDataSourceTests: XCTestCase {
let otherItem = DifferentItem()
section.items.append(otherItem)
- let indexPath = otherItem.indexPath(in: tableViewManager)!
- let drawer = type(of: otherItem).drawer
- let cell = drawer.cell(in: tableViewManager, with: otherItem as Item, for: indexPath)
+ let indexPath = otherItem.indexPath!
+ let drawer = type(of: otherItem).drawer
+ let cell = drawer.cell(in: tableViewManager, with: otherItem as Item, for: indexPath)
XCTAssertTrue(cell is DifferentCell)
}
func testNumberOfSections() {
- let count = self.tableViewManager.numberOfSections(in: self.tableViewManager.tableView)
- expect(count).to(equal(2))
+ let count = dataSource.numberOfSections(in: self.tableViewManager.tableView)
+ expect(count) == 2
}
func testNumberOfRowsInSection() {
- let count = self.tableViewManager.tableView(self.tableViewManager.tableView, numberOfRowsInSection: 0)
+ let count = dataSource.tableView(self.tableViewManager.tableView, numberOfRowsInSection: 0)
expect(count) == 1
}
func testTitleForHeaderInSection() {
- let title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForHeaderInSection: 0)!
+ let title = dataSource.tableView(self.tableViewManager.tableView, titleForHeaderInSection: 0)!
let section = tableViewManager.sections.first!
- expect(HeaderFooterView.title(title)).to(equal(section.header))
+ expect(HeaderFooterView.title(title)) == section.header
}
func testTitleForFooterInSection() {
var title: String?
- title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForFooterInSection: 0)
+ title = dataSource.tableView(self.tableViewManager.tableView, titleForFooterInSection: 0)
let section = tableViewManager.sections.first!
- expect(HeaderFooterView.title(title!)).to(equal(section.footer))
+ expect(HeaderFooterView.title(title!)) == section.footer
- title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForFooterInSection: 1)
+ title = dataSource.tableView(self.tableViewManager.tableView, titleForFooterInSection: 1)
expect(title).to(beNil())
}
- func testViewForHeaderInSection() {
- let view = self.tableViewManager.tableView(self.tableViewManager.tableView, viewForHeaderInSection: 0)
- expect(view).to(beNil())
- }
-
- func testViewForFooterInSection() {
- var view: UIView?
- view = self.tableViewManager.tableView(self.tableViewManager.tableView, viewForFooterInSection: 0)
- expect(view).to(beNil())
-
- view = self.tableViewManager.tableView(self.tableViewManager.tableView, viewForFooterInSection: 1)
- expect(view).notTo(beNil())
- }
}
diff --git a/TableViewKitTests/TableViewDelegateTests.swift b/TableViewKitTests/TableViewDelegateTests.swift
index c249f56..44b6f4e 100644
--- a/TableViewKitTests/TableViewDelegateTests.swift
+++ b/TableViewKitTests/TableViewDelegateTests.swift
@@ -89,7 +89,8 @@ class EditableItem: SelectableItem, Editable {
class TableViewDelegateTests: XCTestCase {
- private var tableViewManager: TableViewManager!
+ fileprivate var tableViewManager: TableViewManager!
+ fileprivate var delegate: TableViewKitDelegate { return tableViewManager.delegate! }
override func setUp() {
super.setUp()
@@ -109,41 +110,41 @@ class TableViewDelegateTests: XCTestCase {
func testEstimatedHeightForHeader() {
var height: CGFloat
- height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForHeaderInSection: 0)
- expect(height).to(beGreaterThan(0.0))
+ height = delegate.tableView(tableViewManager.tableView, estimatedHeightForHeaderInSection: 0)
+ expect(height) > 0.0
- height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForHeaderInSection: 1)
- expect(height).to(equal(tableViewManager.tableView.estimatedSectionHeaderHeight))
+ height = delegate.tableView(tableViewManager.tableView, estimatedHeightForHeaderInSection: 1)
+ expect(height) == tableViewManager.tableView.estimatedSectionHeaderHeight
}
func testHeightForHeader() {
var height: CGFloat
- height = tableViewManager.tableView(tableViewManager.tableView, heightForHeaderInSection: 0)
- expect(height).to(equal(UITableViewAutomaticDimension))
+ height = delegate.tableView(tableViewManager.tableView, heightForHeaderInSection: 0)
+ expect(height) == UITableViewAutomaticDimension
}
func testEstimatedHeightForFooter() {
var height: CGFloat
- height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForFooterInSection: 0)
- expect(height).to(beGreaterThan(0.0))
+ height = delegate.tableView(tableViewManager.tableView, estimatedHeightForFooterInSection: 0)
+ expect(height) > 0.0
- height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForFooterInSection: 1)
- expect(height).to(equal(tableViewManager.tableView.estimatedSectionFooterHeight))
+ height = delegate.tableView(tableViewManager.tableView, estimatedHeightForFooterInSection: 1)
+ expect(height) == tableViewManager.tableView.estimatedSectionFooterHeight
- height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForFooterInSection: 2)
- expect(height).to(equal(44.0))
+ height = delegate.tableView(tableViewManager.tableView, estimatedHeightForFooterInSection: 2)
+ expect(height) == 44.0
}
func testHeightForFooter() {
var height: CGFloat
- height = tableViewManager.tableView(tableViewManager.tableView, heightForFooterInSection: 0)
- expect(height).to(equal(UITableViewAutomaticDimension))
+ height = delegate.tableView(tableViewManager.tableView, heightForFooterInSection: 0)
+ expect(height) == UITableViewAutomaticDimension
- height = tableViewManager.tableView(tableViewManager.tableView, heightForFooterInSection: 2)
- expect(height).to(equal(UITableViewAutomaticDimension))
+ height = delegate.tableView(tableViewManager.tableView, heightForFooterInSection: 2)
+ expect(height) == UITableViewAutomaticDimension
}
func testEstimatedHeightForRowAtIndexPath() {
@@ -151,16 +152,16 @@ class TableViewDelegateTests: XCTestCase {
var indexPath: IndexPath
indexPath = IndexPath(row: 0, section: 0)
- height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForRowAt: indexPath)
- expect(height).to(equal(44.0))
+ height = delegate.tableView(tableViewManager.tableView, estimatedHeightForRowAt: indexPath)
+ expect(height) == 44.0
indexPath = IndexPath(row: 0, section: 1)
- height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForRowAt: indexPath)
- expect(height).to(equal(tableViewManager.tableView.estimatedRowHeight))
+ height = delegate.tableView(tableViewManager.tableView, estimatedHeightForRowAt: indexPath)
+ expect(height) == tableViewManager.tableView.estimatedRowHeight
indexPath = IndexPath(row: 1, section: 1)
- height = tableViewManager.tableView(tableViewManager.tableView, estimatedHeightForRowAt: indexPath)
- expect(height).to(equal(20.0))
+ height = delegate.tableView(tableViewManager.tableView, estimatedHeightForRowAt: indexPath)
+ expect(height) == 20.0
}
func testHeightForRowAtIndexPath() {
@@ -168,37 +169,37 @@ class TableViewDelegateTests: XCTestCase {
var indexPath: IndexPath
indexPath = IndexPath(row: 0, section: 0)
- height = tableViewManager.tableView(tableViewManager.tableView, heightForRowAt: indexPath)
- expect(height).to(equal(UITableViewAutomaticDimension))
+ height = delegate.tableView(tableViewManager.tableView, heightForRowAt: indexPath)
+ expect(height) == UITableViewAutomaticDimension
indexPath = IndexPath(row: 0, section: 1)
- height = tableViewManager.tableView(tableViewManager.tableView, heightForRowAt: indexPath)
- expect(height).to(equal(tableViewManager.tableView.rowHeight))
+ height = delegate.tableView(tableViewManager.tableView, heightForRowAt: indexPath)
+ expect(height) == tableViewManager.tableView.rowHeight
indexPath = IndexPath(row: 1, section: 1)
- height = tableViewManager.tableView(tableViewManager.tableView, heightForRowAt: indexPath)
- expect(height).to(equal(StaticHeigthItem.testStaticHeightValue))
+ height = delegate.tableView(tableViewManager.tableView, heightForRowAt: indexPath)
+ expect(height) == StaticHeigthItem.testStaticHeightValue
}
func testSelectRow() {
var indexPath: IndexPath
indexPath = IndexPath(row: 0, section: 0)
- tableViewManager.tableView(tableViewManager.tableView, didSelectRowAt: indexPath)
+ delegate.tableView(tableViewManager.tableView, didSelectRowAt: indexPath)
let section = tableViewManager.sections[0]
indexPath = IndexPath(row: section.items.count, section: 0)
let item = SelectableItem()
section.items.append(item)
- tableViewManager.tableView(tableViewManager.tableView, didSelectRowAt: indexPath)
- expect(item.check).to(equal(1))
+ delegate.tableView(tableViewManager.tableView, didSelectRowAt: indexPath)
+ expect(item.check) == 1
- item.select(in: tableViewManager, animated: true)
- expect(item.check).to(equal(2))
+ item.select(animated: true)
+ expect(item.check) == 2
- item.deselect(in: tableViewManager, animated: true)
- expect(item.check).to(equal(2))
+ item.deselect(animated: true)
+ expect(item.check) == 2
}
func testEditableRows() {
@@ -210,13 +211,27 @@ class TableViewDelegateTests: XCTestCase {
editableItem.actions = [deleteAction]
section.items.append(editableItem)
- let sectionIndex = section.index(in: tableViewManager)!
- let rows = tableViewManager.tableView(tableViewManager.tableView, numberOfRowsInSection: sectionIndex)
- XCTAssert(rows == 2)
-
- let indexPath = editableItem.indexPath(in: tableViewManager)!
- let actions = tableViewManager.tableView(tableViewManager.tableView, editActionsForRowAt: indexPath)
+ let indexPath = editableItem.indexPath!
+ let actions = delegate.tableView(tableViewManager.tableView, editActionsForRowAt: indexPath)
XCTAssertNotNil(actions)
XCTAssert(actions!.count == 1)
}
+
+ func testViewForHeaderInSection() {
+ let view = delegate.tableView(self.tableViewManager.tableView, viewForHeaderInSection: 0)
+ expect(view).to(beNil())
+ }
+
+ func testViewForFooterInSection() {
+ var view: UIView?
+ view = delegate.tableView(self.tableViewManager.tableView, viewForFooterInSection: 0)
+ expect(view).to(beNil())
+
+ view = delegate.tableView(self.tableViewManager.tableView, viewForFooterInSection: 1)
+ expect(view).to(beNil())
+
+ view = delegate.tableView(self.tableViewManager.tableView, viewForFooterInSection: 2)
+ expect(view).toNot(beNil())
+
+ }
}
diff --git a/TableViewKitTests/TableViewKitTests.swift b/TableViewKitTests/TableViewKitTests.swift
index f08e325..423f02c 100644
--- a/TableViewKitTests/TableViewKitTests.swift
+++ b/TableViewKitTests/TableViewKitTests.swift
@@ -9,6 +9,7 @@ class TestReloadDrawer: CellDrawer {
static internal var type = CellType.class(UITableViewCell.self)
static internal func draw(_ cell: UITableViewCell, with item: Any) {
+ // swiftlint:disable:next force_cast
cell.textLabel?.text = (item as! TestReloadItem).title
}
}
@@ -26,14 +27,14 @@ class EqualableItem: TestReloadItem, Equatable {
self.title = title
}
- public static func ==(lhs: EqualableItem, rhs: EqualableItem) -> Bool {
+ public static func == (lhs: EqualableItem, rhs: EqualableItem) -> Bool {
return lhs.title == rhs.title
}
}
class EquatableSection: NoHeaderFooterSection, Equatable {
- public static func ==(lhs: EquatableSection, rhs: EquatableSection) -> Bool {
+ public static func == (lhs: EquatableSection, rhs: EquatableSection) -> Bool {
return lhs === rhs
}
}
@@ -67,36 +68,39 @@ class TestRegisterHeaderFooterView: UITableViewHeaderFooterView { }
class TableViewKitTests: XCTestCase {
+ var manager: TableViewManager!
+
override func setUp() {
super.setUp()
}
override func tearDown() {
super.tearDown()
+ manager = nil
}
func testAddSection() {
- let tableViewManager = TableViewManager(tableView: UITableView())
+ manager = TableViewManager(tableView: UITableView())
let section = HeaderFooterTitleSection()
- tableViewManager.sections.append(section)
+ manager.sections.append(section)
- expect(tableViewManager.sections.count).to(equal(1))
+ expect(self.manager.sections.count) == 1
}
func testAddItem() {
- let tableViewManager = TableViewManager(tableView: UITableView())
+ manager = TableViewManager(tableView: UITableView())
let item: Item = TestItem()
let section = HeaderFooterTitleSection()
section.items.append(item)
- tableViewManager.sections.insert(section, at: 0)
+ manager.sections.insert(section, at: 0)
- expect(section.items.count).to(equal(1))
- expect(item.section(in: tableViewManager)).notTo(beNil())
+ expect(section.items.count) == 1
+ expect(item.section).notTo(beNil())
section.items.remove(at: 0)
section.items.append(item)
@@ -142,22 +146,22 @@ class TableViewKitTests: XCTestCase {
}
func testRetainCycle() {
- let tableViewManager = TableViewManager(tableView: UITableView())
- tableViewManager.sections.insert(HeaderFooterTitleSection(items: [TestItem()]), at: 0)
+ manager = TableViewManager(tableView: UITableView())
+ manager.sections.insert(HeaderFooterTitleSection(items: [TestItem()]), at: 0)
- weak var section: Section? = tableViewManager.sections.first
+ weak var section: Section? = manager.sections.first
weak var item: Item? = section!.items.first
expect(section).toNot(beNil())
expect(item).toNot(beNil())
- tableViewManager.sections.replace(with: [HeaderFooterTitleSection()])
+ manager.sections.replace(with: [HeaderFooterTitleSection()])
expect(section).to(beNil())
expect(item).to(beNil())
}
func testConvenienceInit() {
- let tableViewManager = TableViewManager(tableView: UITableView(), sections: [HeaderFooterTitleSection()])
+ manager = TableViewManager(tableView: UITableView(), sections: [HeaderFooterTitleSection()])
- expect(tableViewManager.sections.count).to(equal(1))
+ expect(self.manager.sections.count) == 1
}
func testUpdateRow() {
@@ -166,19 +170,20 @@ class TableViewKitTests: XCTestCase {
item.title = "Before"
let section = HeaderFooterTitleSection(items: [item])
- let tableViewManager = TableViewManager(tableView: UITableView(), sections: [section])
+ manager = TableViewManager(tableView: UITableView(), sections: [section])
- guard let indexPath = item.indexPath(in: tableViewManager) else { return }
- var cell = tableViewManager.tableView(tableViewManager.tableView, cellForRowAt: indexPath)
+ guard let indexPath = item.indexPath else { return }
+ let dataSource = manager.dataSource!
+ var cell = dataSource.tableView(manager.tableView, cellForRowAt: indexPath)
- expect(cell.textLabel?.text).to(equal(item.title))
+ expect(cell.textLabel?.text) == item.title
item.title = "After"
- item.reload(in: tableViewManager)
+ item.reload()
- cell = tableViewManager.tableView(tableViewManager.tableView, cellForRowAt: indexPath)
+ cell = dataSource.tableView(manager.tableView, cellForRowAt: indexPath)
- expect(cell.textLabel?.text).to(equal(item.title))
+ expect(cell.textLabel?.text) == item.title
}
func testMoveRows() {
@@ -189,18 +194,18 @@ class TableViewKitTests: XCTestCase {
let item2 = TestItem()
let section = NoHeaderFooterSection(items: [item1, item2])
- let tableViewManager = TableViewManager(tableView: tableView, sections: [section])
+ manager = TableViewManager(tableView: tableView, sections: [section])
- var indexPathItem1 = item1.indexPath(in: tableViewManager)
- var indexPathItem2 = item2.indexPath(in: tableViewManager)
+ var indexPathItem1 = item1.indexPath
+ var indexPathItem2 = item2.indexPath
XCTAssertNotNil(indexPathItem1)
XCTAssertNotNil(indexPathItem2)
section.items.replace(with: [item2, item1])
- indexPathItem1 = item1.indexPath(in: tableViewManager)
- indexPathItem2 = item2.indexPath(in: tableViewManager)
+ indexPathItem1 = item1.indexPath
+ indexPathItem2 = item2.indexPath
XCTAssert(indexPathItem2?.item == 0)
XCTAssert(indexPathItem1?.item == 1)
@@ -213,30 +218,31 @@ class TableViewKitTests: XCTestCase {
let section1 = NoHeaderFooterSection()
let section2 = NoHeaderFooterSection()
- let tableViewManager = TableViewManager(tableView: tableView, sections: [section1, section2])
+ manager = TableViewManager(tableView: tableView, sections: [section1, section2])
- XCTAssert(section1.index(in: tableViewManager) == 0)
- XCTAssert(section2.index(in: tableViewManager) == 1)
+ XCTAssert(section1.index == 0)
+ XCTAssert(section2.index == 1)
- tableViewManager.sections.replace(with: [section2, section1])
+ manager.sections.replace(with: [section2, section1])
- XCTAssert(section1.index(in: tableViewManager) == 1)
- XCTAssert(section2.index(in: tableViewManager) == 0)
+ XCTAssert(section1.index == 1)
+ XCTAssert(section2.index == 0)
}
func testNoCrashOnNonAddedItem() {
- let tableViewManager = TableViewManager(tableView: UITableView(), sections: [HeaderFooterTitleSection()])
+ manager = TableViewManager(tableView: UITableView(), sections: [HeaderFooterTitleSection()])
let item: Item = TestReloadItem()
- item.reload(in: tableViewManager, with: .automatic)
+ item.reload(with: .automatic)
- let section = item.section(in: tableViewManager)
+ let section = item.section
expect(section).to(beNil())
}
func testRegisterNibCells() {
let testBundle = Bundle(for: TableViewKitTests.self)
+ // swiftlint:disable:next line_length
let cellType = CellType.nib(UINib(nibName: String(describing: TestRegisterNibCell.self), bundle: testBundle), TestRegisterNibCell.self)
let tableView = UITableView()
@@ -244,30 +250,34 @@ class TableViewKitTests: XCTestCase {
let cell = tableView.dequeueReusableCell(withIdentifier: cellType.reusableIdentifier)
- expect(cell).toNot(equal(nil))
+ expect(cell).toNot(beNil())
}
func testRegisterNibHeaderFooter() {
let testBundle = Bundle(for: TableViewKitTests.self)
- let headerFooterType = HeaderFooterType.nib(UINib(nibName: String(describing: TestRegisterHeaderFooterView.self), bundle: testBundle), TestRegisterHeaderFooterView.self)
+ let nib = UINib(nibName: String(describing: TestRegisterHeaderFooterView.self), bundle: testBundle)
+ let headerFooterType = HeaderFooterType.nib(nib, TestRegisterHeaderFooterView.self)
let tableView = UITableView()
tableView.register(headerFooterType)
+ // swiftlint:disable:next line_length
let headerFooterView = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerFooterType.reusableIdentifier)
- expect(headerFooterView).toNot(equal(nil))
+ expect(headerFooterView).toNot(beNil())
}
func testNibClassTypeCells() {
let testBundle = Bundle(for: TableViewKitTests.self)
- let type = CellType.nib(UINib(nibName: String(describing: TestRegisterNibCell.self), bundle: testBundle), TestRegisterNibCell.self)
+ let nib = UINib(nibName: String(describing: TestRegisterNibCell.self), bundle: testBundle)
+ let type = CellType.nib(nib, TestRegisterNibCell.self)
_ = type.cellType
}
func testNibClassTypeHeaderFooter() {
let testBundle = Bundle(for: TableViewKitTests.self)
- let type = HeaderFooterType.nib(UINib(nibName: String(describing: TestRegisterHeaderFooterView.self), bundle: testBundle), TestRegisterHeaderFooterView.self)
+ let nib = UINib(nibName: String(describing: TestRegisterHeaderFooterView.self), bundle: testBundle)
+ let type = HeaderFooterType.nib(nib, TestRegisterHeaderFooterView.self)
_ = type.headerFooterType
}
@@ -290,4 +300,32 @@ class TableViewKitTests: XCTestCase {
let registerItem = section.items.first as? TestReloadItem
XCTAssert(registerItem?.title == "Register")
}
+
+ func testManagerProperty() {
+ let tableView = UITableView()
+
+ let item1 = TestItem()
+ XCTAssertNil(item1.manager)
+ let section = NoHeaderFooterSection(items: [item1])
+ XCTAssertNil(section.manager)
+
+ manager = TableViewManager(tableView: tableView, sections: [section])
+ XCTAssert(item1.manager === manager)
+ XCTAssert(section.manager === manager)
+
+ let section2 = NoHeaderFooterSection(items: [item1])
+ manager.sections.append(section2)
+ XCTAssert(section2.manager === manager)
+
+ let item2 = TestItem()
+ section.items.append(item2)
+ XCTAssert(item2.manager === manager)
+
+ let removedSection = manager.sections.removeFirst()
+
+ XCTAssert(removedSection === section)
+ XCTAssertNil(removedSection.manager)
+ XCTAssertNil(removedSection.items[0].manager)
+
+ }
}
diff --git a/build.sh b/build.sh
index 02c9040..27f1a4c 100755
--- a/build.sh
+++ b/build.sh
@@ -4,6 +4,6 @@ set -o pipefail &&
time xcodebuild clean test \
-workspace TableViewKit.xcworkspace \
-scheme TableViewKit \
- -sdk iphonesimulator10.1 \
+ -sdk iphonesimulator10.3 \
-destination 'platform=iOS Simulator,name=iPhone 6s,OS=10.0' \
| xcpretty