Skip to content

Commit

Permalink
Merge branch 'release/0.5.0'
Browse files Browse the repository at this point in the history
adellibovi committed Sep 10, 2016

Verified

This commit was signed with the committer’s verified signature.
adellibovi Alfredo Delli Bovi
2 parents cab297d + 654fe92 commit 1a4a7d0
Showing 46 changed files with 422 additions and 150 deletions.
2 changes: 1 addition & 1 deletion SampleApp/Podfile → Examples/SampleApp/Podfile
Original file line number Diff line number Diff line change
@@ -3,5 +3,5 @@ use_frameworks!

target 'SampleApp' do

pod 'TableViewKit', :path => '..'
pod 'TableViewKit', :path => '../..'
end
Original file line number Diff line number Diff line change
@@ -427,6 +427,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.tableviewkit.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 2.3;
};
name = Debug;
};
@@ -440,6 +441,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.tableviewkit.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 2.3;
};
name = Release;
};
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -9,10 +9,9 @@
import Foundation
import UIKit
import TableViewKit
import ReactiveKit

class SelectionSection: Section {
var items: CollectionProperty<[Item]> = CollectionProperty([])
var items: ObservableArray<Item> = []
weak var tableViewManager: TableViewManager!

required init() { }
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -8,20 +8,34 @@

import UIKit
import TableViewKit
import ReactiveKit

class FirstSection: Section {
var items: CollectionProperty<[Item]> = CollectionProperty([])
var items: ObservableArray<Item> = []
var states: [[Item]] = []

enum State: Int, RawRepresentable {
case preParty
case onParty
case afterParty
}

var currentState: State = .preParty {
didSet {
items.replace(with: states[currentState.rawValue])
}
}

let vc: ViewController

internal var header: HeaderFooter? = CustomHeaderItem(title: "First Section")
internal var footer: HeaderFooter? = CustomHeaderItem(title: "Section Footer\nHola")
internal var header: HeaderFooterView = .view(CustomHeaderItem(title: "First Section"))
internal var footer: HeaderFooterView = .view(CustomHeaderItem(title: "Section Footer\nHola"))

required init(vc: ViewController) {
self.vc = vc

let item = CustomItem(title: "Passengers")
let item2 = CustomItem(title: "Testing")
let item3 = CustomItem(title: "Testing 2")
let dateItem = CustomItem(title: "Birthday")
let selectionItem = CustomItem(title: "Selection")
let textFieldItem = TextFieldItem(placeHolder: "Name")
@@ -44,16 +58,22 @@ class FirstSection: Section {
self.vc.showPickerControl()
}

self.items.insertContentsOf([item, dateItem, selectionItem, textFieldItem, textFieldItem2], at: 0)
states.insert([item, dateItem, selectionItem, textFieldItem, textFieldItem2], atIndex: State.preParty.rawValue)
states.insert([item2, selectionItem, dateItem, item3, textFieldItem2, textFieldItem], atIndex: State.onParty.rawValue)
states.insert([item2], atIndex: State.afterParty.rawValue)
swap(to: .preParty)
}

func swap(to newState: State) {
currentState = newState
}

}

class SecondSection: Section {
var items: CollectionProperty<[Item]> = CollectionProperty([])
var items: ObservableArray<Item> = []

internal var header: HeaderFooter? = CustomHeaderItem(title: "Second Section")
internal var header: HeaderFooterView = .view(CustomHeaderItem(title: "Second Section"))

let vc: ViewController

@@ -82,6 +102,7 @@ class ViewController: UITableViewController {
var tableViewManager: TableViewManager!
var pickerControl: PickerControl?

var firstSection: FirstSection!

override func viewDidLoad() {

@@ -93,7 +114,8 @@ class ViewController: UITableViewController {
self.tableView.estimatedSectionFooterHeight = 100;


tableViewManager = TableViewManager(tableView: self.tableView, sections: [FirstSection(vc: self), SecondSection(vc: self)])
firstSection = FirstSection(vc: self)
tableViewManager = TableViewManager(tableView: self.tableView, sections: [firstSection, SecondSection(vc: self)])

navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Validate", style: .Plain, target: self, action: #selector(validationAction))
}
@@ -133,9 +155,11 @@ class ViewController: UITableViewController {
}

@objc private func validationAction() {
firstSection.swap(to: firstSection.currentState == .preParty ? .onParty : .afterParty)
guard let error = tableViewManager.errors.first else { return }
print(error)

}
}


2 changes: 0 additions & 2 deletions Podfile
Original file line number Diff line number Diff line change
@@ -3,10 +3,8 @@
use_frameworks!

target :'TableViewKit' do
pod 'ReactiveKit', '~> 2.1.1'
end

target :'TableViewKitTests' do
pod 'ReactiveKit', '~> 2.1.1'
pod 'Nimble'
end
3 changes: 1 addition & 2 deletions TableViewKit.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "TableViewKit"
s.version = "0.4.1"
s.version = "0.5.0"
s.summary = "TableView Kit Layer"
s.description = "TableView Kit Layer"
s.homepage = "http://www.edreamsodigeo.com/"
@@ -12,5 +12,4 @@ Pod::Spec.new do |s|
s.resource_bundles = { "TableViewKit" => "TableViewKit/Resources/*.*" }
s.framework = "UIKit", "Foundation"
s.requires_arc = true
s.dependency 'ReactiveKit', '~> 2.1.1'
end
12 changes: 12 additions & 0 deletions TableViewKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
25F08BBD1D7427680033AD0E /* ValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F08BBC1D7427680033AD0E /* ValidatorTests.swift */; };
7892A1603117CFA582BFCAC3 /* Pods_TableViewKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA8FF610F26EA3E790F577BA /* Pods_TableViewKit.framework */; };
AD0E465917E1381DFF2E904B /* Pods_TableViewKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5221BF34A600267BBA677E8E /* Pods_TableViewKitTests.framework */; };
CC5CF11C1D839E71004DECB3 /* Diff.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC5CF11B1D839E71004DECB3 /* Diff.swift */; };
CC97D76D1D741DC4009CDF9D /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC97D76C1D741DC4009CDF9D /* Selectable.swift */; };
CCBE45601D72F69500414A64 /* TableViewKit.h in Headers */ = {isa = PBXBuildFile; fileRef = CCBE455F1D72F69500414A64 /* TableViewKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
CCBE45671D72F69500414A64 /* TableViewKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CCBE455C1D72F69400414A64 /* TableViewKit.framework */; };
@@ -31,6 +32,7 @@
CCCAC1241D7D9E6D0001FC1D /* HeaderFooterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCAC1231D7D9E6D0001FC1D /* HeaderFooterType.swift */; };
CCCAC1261D7D9EE00001FC1D /* HeaderFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCAC1251D7D9EE00001FC1D /* HeaderFooter.swift */; };
CCCAC1281D7DA2D00001FC1D /* ImmutableMutableHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCAC1271D7DA2CF0001FC1D /* ImmutableMutableHeight.swift */; };
CCDB8FAB1D83649B00E44A99 /* ObservableArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCDB8FAA1D83649B00E44A99 /* ObservableArray.swift */; };
CCE400F61D731B7300537715 /* TableViewDataSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE400F51D731B7300537715 /* TableViewDataSourceTests.swift */; };
CCE400FA1D73321600537715 /* ActionBarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCE400F91D73321600537715 /* ActionBarTests.swift */; };
/* End PBXBuildFile section */
@@ -52,6 +54,7 @@
5221BF34A600267BBA677E8E /* Pods_TableViewKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TableViewKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
AA8FF610F26EA3E790F577BA /* Pods_TableViewKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_TableViewKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C6C8E0D2A241295B7A143FE9 /* Pods-TableViewKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TableViewKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-TableViewKitTests/Pods-TableViewKitTests.release.xcconfig"; sourceTree = "<group>"; };
CC5CF11B1D839E71004DECB3 /* Diff.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Diff.swift; sourceTree = "<group>"; };
CC97D76C1D741DC4009CDF9D /* Selectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Selectable.swift; path = Protocols/Selectable.swift; sourceTree = "<group>"; };
CCBE455C1D72F69400414A64 /* TableViewKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TableViewKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CCBE455F1D72F69500414A64 /* TableViewKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TableViewKit.h; sourceTree = "<group>"; };
@@ -76,6 +79,7 @@
CCCAC1231D7D9E6D0001FC1D /* HeaderFooterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderFooterType.swift; sourceTree = "<group>"; };
CCCAC1251D7D9EE00001FC1D /* HeaderFooter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HeaderFooter.swift; path = Protocols/HeaderFooter.swift; sourceTree = "<group>"; };
CCCAC1271D7DA2CF0001FC1D /* ImmutableMutableHeight.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImmutableMutableHeight.swift; sourceTree = "<group>"; };
CCDB8FAA1D83649B00E44A99 /* ObservableArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObservableArray.swift; sourceTree = "<group>"; };
CCE400F51D731B7300537715 /* TableViewDataSourceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableViewDataSourceTests.swift; sourceTree = "<group>"; };
CCE400F91D73321600537715 /* ActionBarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActionBarTests.swift; sourceTree = "<group>"; };
D0E1FB244FF2B149DB428D40 /* Pods-TableViewKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-TableViewKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-TableViewKit/Pods-TableViewKit.release.xcconfig"; sourceTree = "<group>"; };
@@ -162,11 +166,13 @@
CCBE45791D72F79C00414A64 /* Extensions */,
CCBE45821D72F79C00414A64 /* Validator */,
CCBE45761D72F79C00414A64 /* Controls */,
CCDB8FAA1D83649B00E44A99 /* ObservableArray.swift */,
CCBE45891D72F79C00414A64 /* BaseCell.swift */,
CCCAC1271D7DA2CF0001FC1D /* ImmutableMutableHeight.swift */,
CCBE458E1D72F79C00414A64 /* TableViewManager.swift */,
CCBE455F1D72F69500414A64 /* TableViewKit.h */,
CCBE45611D72F69500414A64 /* Info.plist */,
CC5CF11B1D839E71004DECB3 /* Diff.swift */,
);
path = TableViewKit;
sourceTree = "<group>";
@@ -426,8 +432,10 @@
CCBE45971D72F79C00414A64 /* Rules.swift in Sources */,
CC97D76D1D741DC4009CDF9D /* Selectable.swift in Sources */,
CCCAC1281D7DA2D00001FC1D /* ImmutableMutableHeight.swift in Sources */,
CC5CF11C1D839E71004DECB3 /* Diff.swift in Sources */,
CCBE459C1D72F79C00414A64 /* BaseCell.swift in Sources */,
CCCAC1201D7D9E150001FC1D /* CellType.swift in Sources */,
CCDB8FAB1D83649B00E44A99 /* ObservableArray.swift in Sources */,
CCBE458F1D72F79C00414A64 /* ActionBar.swift in Sources */,
CCBE459A1D72F79C00414A64 /* Validator.swift in Sources */,
CCBE45981D72F79C00414A64 /* Validation.swift in Sources */,
@@ -570,6 +578,7 @@
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 2.3;
};
name = Debug;
};
@@ -588,6 +597,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.TableViewKit;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 2.3;
};
name = Release;
};
@@ -599,6 +609,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.TableViewKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 2.3;
};
name = Debug;
};
@@ -610,6 +621,7 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.odigeo.TableViewKitTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 2.3;
};
name = Release;
};
97 changes: 97 additions & 0 deletions TableViewKit/Diff.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//
// Diff.swift
// TableViewKit
//
// Created by Alfredo Delli Bovi on 10/09/2016.
// Copyright © 2016 odigeo. All rights reserved.
//

import Foundation

struct Diff {
public var inserts: [Int]
public var deletes: [Int]
}


class DiffIterator : GeneratorType {
struct Coordinates {
var x: Int
var y: Int
}
var last: Coordinates
private let matrix: [[Int]]

init(matrix: [[Int]]){
self.matrix = matrix
self.last = Coordinates(x: matrix.count-1, y: matrix.first!.count-1)
}

func next() -> ArrayChanges? {
while(last.x > 0 || last.y > 0) {
if last.x == 0 {
last.y -= 1
return .inserts([last.y])
} else if last.y == 0 {
last.x -= 1
return .deletes([last.x])
} else if matrix[last.x][last.y] == matrix[last.x][last.y - 1] {
last.y -= 1
return .inserts([last.y])
} else if matrix[last.x][last.y] == matrix[last.x - 1][last.y] {
last.x -= 1
return .deletes([last.x])
} else {
last.x -= 1
last.y -= 1
}
}
return nil
}
}

class DiffSequence : SequenceType {
private let matrix: [[Int]]

init(matrix: [[Int]]){
self.matrix = matrix
}

func generate() -> DiffIterator {
return DiffIterator(matrix: matrix)
}
}



extension Array {

static func diff(between x: [Element], and y: [Element], where predicate: Predicate) -> Diff {
var matrix = [[Int]](count: x.count+1, repeatedValue: [Int](count: y.count+1, repeatedValue: 0))
for (i, xElem) in x.enumerate() {
for (j, yElem) in y.enumerate() {
if predicate(xElem, yElem) {
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])
}
}
}

let changes = [ArrayChanges](DiffSequence(matrix: matrix))
let inserts: [Int] = changes.flatMap { change -> [Int] in
guard case .inserts(let array) = change else { return [] }
return array
}.sort { $0 > $1 }


let deletes: [Int] = changes.flatMap { change -> [Int] in
guard case .deletes(let array) = change else { return [] }
return array
}.sort { $0 < $1 }


return Diff(inserts: inserts, deletes: deletes)
}

}
7 changes: 7 additions & 0 deletions TableViewKit/Extensions/UITableView+Register.swift
Original file line number Diff line number Diff line change
@@ -26,4 +26,11 @@ public extension UITableView {
registerNib(nib, forHeaderFooterViewReuseIdentifier: type.reusableIdentifier)
}
}

func moveRows(at indexPaths: [NSIndexPath], to newIndexPaths: [NSIndexPath]) {
for (index, _) in indexPaths.enumerate() {
moveRowAtIndexPath(indexPaths[index], toIndexPath: newIndexPaths[index])
}
}

}
118 changes: 118 additions & 0 deletions TableViewKit/ObservableArray.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// ObservableArray.swift
// TableViewKit
//
// Created by Alfredo Delli Bovi on 09/09/2016.
//
//

import Foundation

typealias Predicate = (Any, Any) -> Bool

enum ArrayChanges {
case inserts([Int])
case deletes([Int])
case updates([Int])
case moves([(Int, Int)])
case beginUpdates
case endUpdates
}

public struct ObservableArray<T>: ArrayLiteralConvertible, CollectionType, MutableCollectionType, RangeReplaceableCollectionType {

public typealias Element = T

private var _array: [T] {
willSet {
callback?(.beginUpdates)
}
didSet {
let newArray = _array
var oldArray = oldValue
_arraySet(oldArray, newArray: newArray)
callback?(.endUpdates)
}
}

private func _arraySet(oldArray: [T], newArray: [T]) {
var oldArray = oldArray
let moves = newArray.enumerate().flatMap { (toIndex, element) -> (Int, Int)? in
let anyElement = element as! AnyObject
guard let fromIndex = oldArray.indexOf({ $0 as! AnyObject === anyElement }) where
fromIndex != toIndex else { return nil }

oldArray.removeAtIndex(fromIndex)
if (toIndex >= oldArray.count) {
oldArray.append(element)
} else {
oldArray.insert(element, atIndex: toIndex)
}
return (fromIndex, toIndex)
}

let equals: Predicate = { lhs, rhs in
let lhs = lhs as! AnyObject
let rhs = rhs as! AnyObject
return lhs === rhs
}
let diff = Array.diff(between: oldArray, and: newArray, where: equals)
let deletes = diff.deletes
let inserts = diff.inserts

callback?(.moves(moves))
callback?(.deletes(deletes))
callback?(.inserts(inserts))
}

var callback: ((ArrayChanges) -> ())?

public init() {
self._array = []
}

public init(array: [T]) {
self._array = array
}

public init(arrayLiteral elements: Element...) {
self._array = elements
}

public func generate() -> Array<T>.Generator {
return _array.generate()
}

public var startIndex: Int {
return _array.startIndex
}

public var endIndex: Int {
return _array.endIndex
}

public var isEmpty: Bool {
return _array.isEmpty
}

public var count: Int {
return _array.count
}

public subscript(index: Int) -> T {
get {
return _array[index]
}
set {
_array[index] = newValue
}
}

public mutating func replaceRange<C where C : CollectionType, C.Generator.Element == T>(_ subrange: Range<Int>, with newElements: C) {
_array.replaceRange(subrange, with: newElements)
}

public mutating func replace(with array: [T]) {
_array = array
}
}
22 changes: 22 additions & 0 deletions TableViewKit/Protocols/HeaderFooter.swift
Original file line number Diff line number Diff line change
@@ -8,6 +8,28 @@

import Foundation

public enum HeaderFooterView: NilLiteralConvertible, Equatable {
case title(String)
case view(HeaderFooter)
case none

public init(nilLiteral: ()) {
self = .none
}
}
public func == (lhs: HeaderFooterView, rhs: HeaderFooterView) -> Bool {
switch (lhs, rhs) {
case (.none, .none):
return true
case (.view(let lhs), .view(let rhs)):
return lhs === rhs
case (.title(let lhs), .title(let rhs)):
return lhs == rhs
default:
return false
}
}

public protocol HeaderFooter: class {
var drawer: HeaderFooterDrawer.Type { get }
var height: ImmutableMutableHeight? { get }
55 changes: 27 additions & 28 deletions TableViewKit/Section.swift
Original file line number Diff line number Diff line change
@@ -8,35 +8,29 @@

import Foundation
import UIKit
import ReactiveKit


public protocol Section: class {
var items: CollectionProperty<[Item]> { get }
var items: ObservableArray<Item> { get set }

var headerTitle: String? { get }
var footerTitle: String? { get }
var header: HeaderFooter? { get }
var footer: HeaderFooter? { get }
var header: HeaderFooterView { get }
var footer: HeaderFooterView { get }

func index(inManager manager: TableViewManager) -> Int?
func setup(inManager manager: TableViewManager)
func register(inManager manager: TableViewManager)
}

extension Section {
public var headerTitle: String? { return nil }
public var footerTitle: String? { return nil }
public var header: HeaderFooter? { return nil }
public var footer: HeaderFooter? { return nil }
public var header: HeaderFooterView { return nil }
public var footer: HeaderFooterView { return nil }

public func index(inManager manager: TableViewManager) -> Int? { return manager.sections.indexOf(self) }
public func register(inManager manager: TableViewManager) {
if let header = header {
if case .view(let header) = header {
manager.tableView.register(type: header.drawer.headerFooterType)
}
if let header = header {
manager.tableView.register(type: header.drawer.headerFooterType)
if case .view(let footer) = footer {
manager.tableView.register(type: footer.drawer.headerFooterType)
}
items.forEach {
if let item = $0 as? Validationable {
@@ -48,27 +42,32 @@ extension Section {
}

public func setup(inManager manager: TableViewManager) {
items.observeNext { e in
items.callback = { change in
guard let sectionIndex = manager.sections.indexOf(self) else { return }
let tableView = manager.tableView

tableView.beginUpdates()
if e.inserts.count > 0 {
let indexPaths = e.inserts.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
switch change {
case .inserts(let array):

let indexPaths = array.map { NSIndexPath(forRow: $0, inSection: sectionIndex) }
tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}

if e.updates.count > 0 {
let indexPaths = e.updates.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
case .deletes(let array):
let indexPaths = array.map { NSIndexPath(forRow: $0, inSection: sectionIndex) }
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
case .updates(let array):
let indexPaths = array.map { NSIndexPath(forRow: $0, inSection: sectionIndex) }
tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
case .moves(let array):
let fromIndexPaths = array.map { NSIndexPath(forRow: $0.0, inSection: sectionIndex) }
let toIndexPaths = array.map { NSIndexPath(forRow: $0.1, inSection: sectionIndex) }
tableView.moveRows(at: fromIndexPaths, to: toIndexPaths)
case .beginUpdates:
tableView.beginUpdates()
case .endUpdates:
tableView.endUpdates()
}

if e.deletes.count > 0 {
let indexPaths = e.deletes.map { NSIndexPath(forItem: $0, inSection: sectionIndex) }
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Automatic)
}
tableView.endUpdates()
}.disposeIn(manager.disposeBag)
}
}
}

194 changes: 95 additions & 99 deletions TableViewKit/TableViewManager.swift
Original file line number Diff line number Diff line change
@@ -8,13 +8,12 @@

import Foundation
import UIKit
import ReactiveKit

public class TableViewManager: NSObject {

// MARK: Properties
public let tableView: UITableView
public var sections: CollectionProperty<[Section]> = CollectionProperty([])
public var sections: ObservableArray<Section> = []

public var validator: ValidatorManager<String?> = ValidatorManager()
public var errors: [ValidationError] {
@@ -23,8 +22,6 @@ public class TableViewManager: NSObject {
}
}

let disposeBag = DisposeBag()


// MARK: Inits

@@ -34,49 +31,105 @@ public class TableViewManager: NSObject {
self.tableView.dataSource = self
self.tableView.delegate = self

sections.observeNext { e in
guard e.inserts.count + e.updates.count + e.deletes.count > 0 else { return }

e.inserts.forEach { index in
e.collection[index].setup(inManager: self)
e.collection[index].register(inManager: self)
}

tableView.beginUpdates()
if e.inserts.count > 0 {
tableView.insertSections(NSIndexSet(e.inserts), withRowAnimation: .Automatic)
}

if e.updates.count > 0 {
tableView.reloadSections(NSIndexSet(e.updates), withRowAnimation: .Automatic)
sections.callback = { change in

switch change {
case .inserts(let array):
array.forEach { index in
self.sections[index].setup(inManager: self)
self.sections[index].register(inManager: self)
}
tableView.insertSections(NSIndexSet(array), withRowAnimation: .Automatic)
case .deletes(let array):
tableView.deleteSections(NSIndexSet(array), withRowAnimation: .Automatic)
case .updates(let array):
tableView.reloadSections(NSIndexSet(array), withRowAnimation: .Automatic)
case .moves(_): break
case .beginUpdates:
tableView.beginUpdates()
case .endUpdates:
tableView.endUpdates()
}

if e.deletes.count > 0 {
tableView.deleteSections(NSIndexSet(e.deletes), withRowAnimation: .Automatic)
}
tableView.endUpdates()
}.disposeIn(disposeBag)
}
}

public convenience init(tableView: UITableView, sections: [Section]) {
self.init(tableView: tableView)
self.sections.insertContentsOf(sections, at: 0)
}

}

extension TableViewManager {

private func item(forIndexPath indexPath: NSIndexPath) -> Item {
return sections[indexPath.section].items[indexPath.row]
}

private func header(inSection section: Int) -> HeaderFooter? {
return sections[section].header

private func view(forKey key: (Section) -> HeaderFooterView, inSection section: Int) -> UIView? {
guard case .view(let item) = key(sections[section]) else { return nil }

let drawer = item.drawer
let view = drawer.view(inManager: self, withItem: item)
drawer.draw(view, withItem: item)

return view
}

private func title(forKey key: (Section) -> HeaderFooterView, inSection section: Int) -> String? {
if case .title(let value) = key(sections[section]) {
return value
}
return nil

}

private func estimatedHeight(forKey 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 estimatedHeight(height)
case .title(_):
return 1.0
default:
return nil
}
}

private func estimatedHeight(atIndexPath indexPath: NSIndexPath) -> CGFloat? {
guard let height = item(forIndexPath: indexPath).height else { return nil }
return estimatedHeight(height)
}

private func estimatedHeight(height: ImmutableMutableHeight) -> CGFloat {
switch height {
case .immutable(_):
return 0.0
case .mutable(let value):
return value
}
}

private func height(forKey key: (Section) -> HeaderFooterView, inSection section: Int) -> CGFloat? {
guard case .view(let view) = key(sections[section]), let value = view.height
else { return nil }
return height(value)
}

private func height(atIndexPath indexPath: NSIndexPath) -> CGFloat? {
guard let value = item(forIndexPath: indexPath).height else { return nil }
return height(value)
}

private func footer(inSection section: Int) -> HeaderFooter? {
return sections[section].footer

private func height(height: ImmutableMutableHeight) -> CGFloat {
switch height {
case .immutable(let value):
return value
case .mutable(_):
return UITableViewAutomaticDimension
}
}

}
@@ -105,12 +158,12 @@ extension TableViewManager: UITableViewDataSource {


public func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return sections[section].headerTitle
return title(forKey: {$0.header}, inSection: section)
}


public func tableView(tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return sections[section].footerTitle
return title(forKey: {$0.footer}, inSection: section)
}


@@ -124,92 +177,35 @@ extension TableViewManager: UITableViewDelegate {
}

public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
guard let height = item(forIndexPath: indexPath).height else { return tableView.rowHeight }
switch height {
case .immutable(let value):
return value
case .mutable(_):
return UITableViewAutomaticDimension
}
return height(atIndexPath: indexPath) ?? tableView.rowHeight
}

public func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
guard let currentItem = header(inSection: section), let height = currentItem.height
else { return tableView.sectionHeaderHeight }

switch height {
case .immutable(let value):
return value
case .mutable(_):
return UITableViewAutomaticDimension
}
return height(forKey: {$0.header}, inSection: section) ?? tableView.sectionHeaderHeight
}

public func tableView(tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
guard let currentItem = footer(inSection: section), let height = currentItem.height
else { return tableView.sectionFooterHeight }

switch height {
case .immutable(let value):
return value
case .mutable(_):
return UITableViewAutomaticDimension
}
return height(forKey: {$0.footer}, inSection: section) ?? tableView.sectionFooterHeight
}

public func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
guard let height = item(forIndexPath: indexPath).height else { return tableView.estimatedRowHeight }
switch height {
case .immutable(_):
return 0.0
case .mutable(let value):
return value
}
return estimatedHeight(atIndexPath: indexPath) ?? tableView.estimatedRowHeight
}

public func tableView(tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat {
guard let currentItem = header(inSection: section), let height = currentItem.height
else { return tableView.estimatedSectionHeaderHeight }

switch height {
case .immutable(_):
return 0.0
case .mutable(let value):
return value
}
return estimatedHeight(forKey: {$0.header}, inSection: section) ?? tableView.estimatedSectionHeaderHeight
}

public func tableView(tableView: UITableView, estimatedHeightForFooterInSection section: Int) -> CGFloat {
guard let currentItem = footer(inSection: section), let height = currentItem.height
else { return tableView.estimatedSectionFooterHeight }

switch height {
case .immutable(_):
return 0.0
case .mutable(let value):
return value
}
return estimatedHeight(forKey: {$0.footer}, inSection: section) ?? tableView.estimatedSectionHeaderHeight
}

public func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let currentItem = header(inSection: section) else { return nil }

let drawer = currentItem.drawer
let view = drawer.view(inManager: self, withItem: currentItem)
drawer.draw(view, withItem: currentItem)

return view
return view(forKey: {$0.header}, inSection: section)
}

public func tableView(tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
guard let currentItem = footer(inSection: section) else { return nil }

let drawer = currentItem.drawer
let view = drawer.view(inManager: self, withItem: currentItem)
drawer.draw(view, withItem: currentItem)

return view

return view(forKey: {$0.footer}, inSection: section)
}

}
15 changes: 7 additions & 8 deletions TableViewKitTests/TableViewDataSourceTests.swift
Original file line number Diff line number Diff line change
@@ -9,14 +9,13 @@
import XCTest
import TableViewKit
import Nimble
import ReactiveKit

class TestSection: Section {
var items: CollectionProperty<[Item]> = CollectionProperty([])
var items: ObservableArray<Item> = []
weak var tableViewManager: TableViewManager!

internal var headerTitle: String? { return "Header" }
internal var footerTitle: String? { return "Footer" }
internal var header: HeaderFooterView { return .title("Header") }
internal var footer: HeaderFooterView { return .title("Footer") }

convenience init(items: [Item]) {
self.init()
@@ -78,13 +77,13 @@ class TableViewDataSourceTests: XCTestCase {
}

func testTitleForHeaderInSection() {
let title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForHeaderInSection: 0)
expect(title).to(equal(section.headerTitle))
let title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForHeaderInSection: 0)!
expect(HeaderFooterView.title(title)).to(equal(section.header))
}

func testTitleForFooterInSection() {
let title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForFooterInSection: 0)
expect(title).to(equal(section.footerTitle))
let title = self.tableViewManager.tableView(self.tableViewManager.tableView, titleForFooterInSection: 0)!
expect(HeaderFooterView.title(title)).to(equal(section.footer))
}

func testViewForHeaderInSection() {

0 comments on commit 1a4a7d0

Please sign in to comment.