commit
c9bd52797d
16
CHANGELOG.md
16
CHANGELOG.md
|
|
@ -2,8 +2,20 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [2.0.0](https://github.com/maxsokolov/TableKit/releases/tag/1.4.0)
|
||||
Released on 2016-09-06. Breaking changes in 2.0.0:
|
||||
## [2.3.0](https://github.com/maxsokolov/TableKit/releases/tag/2.3.0)
|
||||
Released on 2016-11-16.
|
||||
- `shouldUsePrototypeCellHeightCalculation` moved to `TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation: true)`
|
||||
- Prototype cell height calculation bugfixes
|
||||
|
||||
## [2.1.0](https://github.com/maxsokolov/TableKit/releases/tag/2.1.0)
|
||||
Released on 2016-10-19.
|
||||
- `action` method was deprecated on TableRow. Use `on` instead.
|
||||
- Support multiple actions with same type on row.
|
||||
- You could now build your own cell height calculating strategy. See [TablePrototypeCellHeightCalculator](Sources/TablePrototypeCellHeightCalculator.swift).
|
||||
- Default distance between sections changed to `UITableViewAutomaticDimension`. You can customize it, see [TableSection](Sources/TableSection.swift)
|
||||
|
||||
## [2.0.0](https://github.com/maxsokolov/TableKit/releases/tag/2.0.0)
|
||||
Released on 2016-10-06. Breaking changes in 2.0.0:
|
||||
<br/>The signatures of `TableRow` and `TableRowAction` classes were changed from
|
||||
```swift
|
||||
let action = TableRowAction<String, StringTableViewCell>(.click) { (data) in
|
||||
|
|
|
|||
|
|
@ -13,12 +13,31 @@ class AutolayoutCellsController: UIViewController {
|
|||
|
||||
@IBOutlet weak var tableView: UITableView! {
|
||||
didSet {
|
||||
tableDirector = TableDirector(tableView: tableView)
|
||||
tableDirector.shouldUsePrototypeCellHeightCalculation = true
|
||||
tableDirector = TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation: true)
|
||||
}
|
||||
}
|
||||
var tableDirector: TableDirector!
|
||||
|
||||
func randomString(length: Int) -> String {
|
||||
|
||||
let letters : NSString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
let len = UInt32(letters.length)
|
||||
|
||||
var randomString = ""
|
||||
|
||||
for _ in 0 ..< length {
|
||||
let rand = arc4random_uniform(len)
|
||||
var nextChar = letters.character(at: Int(rand))
|
||||
randomString += NSString(characters: &nextChar, length: 1) as String
|
||||
}
|
||||
|
||||
return randomString
|
||||
}
|
||||
|
||||
func randomInt(min: Int, max:Int) -> Int {
|
||||
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
|
|
@ -27,10 +46,10 @@ class AutolayoutCellsController: UIViewController {
|
|||
let section = TableSection()
|
||||
|
||||
var rows = 0
|
||||
while rows <= 1000 {
|
||||
while rows <= 20 {
|
||||
rows += 1
|
||||
|
||||
let row = TableRow<AutolayoutTableViewCell>(item: ())
|
||||
let row = TableRow<AutolayoutTableViewCell>(item: randomString(length: randomInt(min: 20, max: 100)))
|
||||
section += row
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ class MainController: UIViewController {
|
|||
|
||||
title = "TableKit"
|
||||
|
||||
let clickAction = TableRowAction<ConfigurableTableViewCell>(.click) { [weak self] (data) in
|
||||
let clickAction = TableRowAction<ConfigurableTableViewCell>(.click) { [weak self] (options) in
|
||||
|
||||
switch (data.indexPath as NSIndexPath).row {
|
||||
switch options.indexPath.row {
|
||||
case 0:
|
||||
self?.performSegue(withIdentifier: "autolayoutcells", sender: nil)
|
||||
case 1:
|
||||
|
|
@ -34,11 +34,16 @@ class MainController: UIViewController {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
let printClickAction = TableRowAction<ConfigurableTableViewCell>(.click) { (options) in
|
||||
|
||||
print("click", options.indexPath)
|
||||
}
|
||||
|
||||
let rows = [
|
||||
|
||||
TableRow<ConfigurableTableViewCell>(item: "Autolayout cells", actions: [clickAction]),
|
||||
TableRow<ConfigurableTableViewCell>(item: "Nib cells", actions: [clickAction])
|
||||
TableRow<ConfigurableTableViewCell>(item: "Autolayout cells", actions: [clickAction, printClickAction]),
|
||||
TableRow<ConfigurableTableViewCell>(item: "Nib cells", actions: [clickAction, printClickAction])
|
||||
]
|
||||
|
||||
// automatically creates a section, also could be used like tableDirector.append(rows: rows)
|
||||
|
|
|
|||
|
|
@ -21,12 +21,13 @@ class NibCellsController: UITableViewController {
|
|||
tableDirector = TableDirector(tableView: tableView)
|
||||
|
||||
let numbers = [1000, 2000, 3000, 4000, 5000]
|
||||
|
||||
let shouldHighlightAction = TableRowAction<NibTableViewCell>(.shouldHighlight) { (_) -> Bool in
|
||||
return false
|
||||
}
|
||||
|
||||
let rows = numbers.map { TableRow<NibTableViewCell>(item: $0, actions: [shouldHighlightAction]) }
|
||||
let rows = numbers.map {
|
||||
TableRow<NibTableViewCell>(item: $0)
|
||||
.on(.shouldHighlight) { (_) -> Bool in
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
tableDirector.append(rows: rows)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,23 +10,22 @@ import UIKit
|
|||
import TableKit
|
||||
|
||||
private let LoremIpsumTitle = "Lorem ipsum dolor sit amet, consectetur adipisicing elit"
|
||||
private let LoremIpsumBody = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eius adipisci, sed libero. Iste asperiores suscipit, consequatur debitis animi impedit numquam facilis iusto porro labore dolorem, maxime magni incidunt. Delectus, est! Totam at eius excepturi deleniti sed, error repellat itaque omnis maiores tempora ratione dolor velit minus porro aspernatur repudiandae labore quas adipisci esse, nulla tempore voluptatibus cupiditate. Ab provident, atque. Possimus deserunt nisi perferendis, consequuntur odio et aperiam, est, dicta dolor itaque sunt laborum, magni qui optio illum dolore laudantium similique harum. Eveniet quis, libero eligendi delectus repellendus repudiandae ipsum? Vel nam odio dolorem, voluptas sequi minus quo tempore, animi est quia earum maxime. Reiciendis quae repellat, modi non, veniam natus soluta at optio vitae in excepturi minima eveniet dolor."
|
||||
|
||||
class AutolayoutTableViewCell: UITableViewCell, ConfigurableCell {
|
||||
|
||||
typealias T = Void
|
||||
typealias T = String
|
||||
|
||||
@IBOutlet var titleLabel: UILabel!
|
||||
@IBOutlet var subtitleLabel: UILabel!
|
||||
|
||||
static var estimatedHeight: CGFloat? {
|
||||
return 700
|
||||
return 150
|
||||
}
|
||||
|
||||
|
||||
func configure(with string: T) {
|
||||
|
||||
titleLabel.text = LoremIpsumTitle
|
||||
subtitleLabel.text = LoremIpsumBody
|
||||
subtitleLabel.text = string
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
|
|
@ -37,4 +36,4 @@ class AutolayoutTableViewCell: UITableViewCell, ConfigurableCell {
|
|||
titleLabel.preferredMaxLayoutWidth = titleLabel.bounds.size.width
|
||||
subtitleLabel.preferredMaxLayoutWidth = subtitleLabel.bounds.size.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
40
README.md
40
README.md
|
|
@ -4,7 +4,7 @@
|
|||
<a href="https://travis-ci.org/maxsokolov/TableKit"><img src="https://api.travis-ci.org/maxsokolov/TableKit.svg" alt="Build Status" /></a>
|
||||
<a href="https://developer.apple.com/swift"><img src="https://img.shields.io/badge/Swift_3.0-compatible-4BC51D.svg?style=flat" alt="Swift 3.0 compatible" /></a>
|
||||
<a href="https://github.com/Carthage/Carthage"><img src="https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat" alt="Carthage compatible" /></a>
|
||||
<a href="https://cocoapods.org/pods/tablekit"><img src="https://img.shields.io/badge/pod-2.0.0-blue.svg" alt="CocoaPods compatible" /></a>
|
||||
<a href="https://cocoapods.org/pods/tablekit"><img src="https://img.shields.io/badge/pod-2.3.1-blue.svg" alt="CocoaPods compatible" /></a>
|
||||
<img src="https://img.shields.io/badge/platform-iOS-blue.svg?style=flat" alt="Platform iOS" />
|
||||
<a href="https://raw.githubusercontent.com/maxsokolov/tablekit/master/LICENSE"><img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License: MIT" /></a>
|
||||
</p>
|
||||
|
|
@ -84,13 +84,14 @@ You could have as many rows and sections as you need.
|
|||
|
||||
It nice to have some actions that related to your cells:
|
||||
```swift
|
||||
let action = TableRowAction<StringTableViewCell>(.click) { (data) in
|
||||
let action = TableRowAction<StringTableViewCell>(.click) { (options) in
|
||||
|
||||
// you could access any useful information that relates to the action
|
||||
|
||||
// data.cell - StringTableViewCell?
|
||||
// data.item - String
|
||||
// data.indexPath - IndexPath
|
||||
// options.cell - StringTableViewCell?
|
||||
// options.item - String
|
||||
// options.indexPath - IndexPath
|
||||
// options.userInfo - [AnyHashable: Any]?
|
||||
}
|
||||
|
||||
let row = TableRow<StringTableViewCell>(item: "some", actions: [action])
|
||||
|
|
@ -98,10 +99,10 @@ let row = TableRow<StringTableViewCell>(item: "some", actions: [action])
|
|||
Or, using nice chaining approach:
|
||||
```swift
|
||||
let row = TableRow<StringTableViewCell>(item: "some")
|
||||
.action(.click) { (data) in
|
||||
.on(.click) { (options) in
|
||||
|
||||
}
|
||||
.action(.shouldHighlight) { (data) -> Bool in
|
||||
.on(.shouldHighlight) { (options) -> Bool in
|
||||
return false
|
||||
}
|
||||
```
|
||||
|
|
@ -126,10 +127,29 @@ class MyTableViewCell: UITableViewCell, ConfigurableCell {
|
|||
```
|
||||
And handle them accordingly:
|
||||
```swift
|
||||
let myAction = TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)) { (data) in
|
||||
let myAction = TableRowAction<MyTableViewCell>(.custom(MyActions.ButtonClicked)) { (options) in
|
||||
|
||||
}
|
||||
```
|
||||
## Multiple actions with same type
|
||||
|
||||
It's also possible to use multiple actions with same type:
|
||||
```swift
|
||||
let click1 = TableRowAction<StringTableViewCell>(.click) { (options) in }
|
||||
click1.id = "click1" // optional
|
||||
|
||||
let click2 = TableRowAction<StringTableViewCell>(.click) { (options) in }
|
||||
click2.id = "click2" // optional
|
||||
|
||||
let row = TableRow<StringTableViewCell>(item: "some", actions: [click1, click2])
|
||||
```
|
||||
Could be useful in case if you want to separate your logic somehow. Actions will be invoked in order which they were attached.
|
||||
> If you define multiple actions with same type which also return a value, only last return value will be used for table view.
|
||||
|
||||
You could also remove any action by id:
|
||||
```swift
|
||||
row.removeAction(forActionId: "action_id")
|
||||
```
|
||||
|
||||
# Advanced
|
||||
|
||||
|
|
@ -147,9 +167,9 @@ class StringTableViewCell: UITableViewCell, ConfigurableCell {
|
|||
```
|
||||
It's enough for most cases. But you may be not happy with this. So you could use a prototype cell to calculate cells heights. To enable this feature simply use this property:
|
||||
```swift
|
||||
tableDirector.shouldUsePrototypeCellHeightCalculation = true
|
||||
let tableDirector = TableDirector(tableView: tableView, shouldUsePrototypeCellHeightCalculation: true)
|
||||
```
|
||||
It does all dirty work with prototypes for you [behind the scene](Sources/HeightStrategy.swift), so you don't have to worry about anything except of your cell configuration:
|
||||
It does all dirty work with prototypes for you [behind the scene](Sources/TablePrototypeCellHeightCalculator.swift), so you don't have to worry about anything except of your cell configuration:
|
||||
```swift
|
||||
class ImageTableViewCell: UITableViewCell, ConfigurableCell {
|
||||
|
||||
|
|
|
|||
|
|
@ -34,22 +34,14 @@ public protocol ConfigurableCell {
|
|||
public extension ConfigurableCell where Self: UITableViewCell {
|
||||
|
||||
static var reuseIdentifier: String {
|
||||
get {
|
||||
return String(describing: self)
|
||||
}
|
||||
|
||||
return String(describing: self)
|
||||
}
|
||||
|
||||
static var estimatedHeight: CGFloat? {
|
||||
get {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
static var defaultHeight: CGFloat? {
|
||||
get {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,10 +20,6 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
struct TableKitNotifications {
|
||||
static let CellAction = "TableKitNotificationsCellAction"
|
||||
}
|
||||
|
||||
/**
|
||||
A custom action that you can trigger from your cell.
|
||||
You can easily catch actions using a chaining manner with your row.
|
||||
|
|
|
|||
|
|
@ -26,16 +26,20 @@ import UIKit
|
|||
open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
||||
|
||||
open private(set) weak var tableView: UITableView?
|
||||
open private(set) var sections = [TableSection]()
|
||||
open fileprivate(set) var sections = [TableSection]()
|
||||
|
||||
private weak var scrollDelegate: UIScrollViewDelegate?
|
||||
private var heightStrategy: CellHeightCalculatable?
|
||||
private var cellRegisterer: TableCellRegisterer?
|
||||
public private(set) var rowHeightCalculator: RowHeightCalculator?
|
||||
private var sectionsIndexTitlesIndexes: [Int]?
|
||||
|
||||
@available(*, deprecated, message: "Produced incorrect behaviour")
|
||||
open var shouldUsePrototypeCellHeightCalculation: Bool = false {
|
||||
didSet {
|
||||
if shouldUsePrototypeCellHeightCalculation {
|
||||
heightStrategy = PrototypeHeightStrategy(tableView: tableView)
|
||||
rowHeightCalculator = TablePrototypeCellHeightCalculator(tableView: tableView)
|
||||
} else {
|
||||
rowHeightCalculator = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -44,21 +48,29 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
return sections.isEmpty
|
||||
}
|
||||
|
||||
public init(tableView: UITableView, scrollDelegate: UIScrollViewDelegate? = nil, shouldUseAutomaticCellRegistration: Bool = true) {
|
||||
public init(tableView: UITableView, scrollDelegate: UIScrollViewDelegate? = nil, shouldUseAutomaticCellRegistration: Bool = true, cellHeightCalculator: RowHeightCalculator?) {
|
||||
super.init()
|
||||
|
||||
if shouldUseAutomaticCellRegistration {
|
||||
self.cellRegisterer = TableCellRegisterer(tableView: tableView)
|
||||
}
|
||||
|
||||
self.rowHeightCalculator = cellHeightCalculator
|
||||
self.scrollDelegate = scrollDelegate
|
||||
self.tableView = tableView
|
||||
self.tableView?.delegate = self
|
||||
self.tableView?.dataSource = self
|
||||
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(didReceiveAction), name: NSNotification.Name(rawValue: TableKitNotifications.CellAction), object: nil)
|
||||
}
|
||||
|
||||
public convenience init(tableView: UITableView, scrollDelegate: UIScrollViewDelegate? = nil, shouldUseAutomaticCellRegistration: Bool = true, shouldUsePrototypeCellHeightCalculation: Bool = false) {
|
||||
|
||||
let heightCalculator: TablePrototypeCellHeightCalculator? = shouldUsePrototypeCellHeightCalculation ? TablePrototypeCellHeightCalculator(tableView: tableView) : nil
|
||||
|
||||
self.init(tableView: tableView, scrollDelegate: scrollDelegate, shouldUseAutomaticCellRegistration: shouldUseAutomaticCellRegistration, cellHeightCalculator: heightCalculator)
|
||||
}
|
||||
|
||||
deinit {
|
||||
NotificationCenter.default.removeObserver(self)
|
||||
}
|
||||
|
|
@ -70,8 +82,8 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
// MARK: Public
|
||||
|
||||
@discardableResult
|
||||
open func invoke(action: TableRowActionType, cell: UITableViewCell?, indexPath: IndexPath) -> Any? {
|
||||
return sections[indexPath.section].rows[indexPath.row].invoke(action, cell: cell, path: indexPath)
|
||||
open func invoke(action: TableRowActionType, cell: UITableViewCell?, indexPath: IndexPath, userInfo: [AnyHashable: Any]? = nil) -> Any? {
|
||||
return sections[indexPath.section].rows[indexPath.row].invoke(action: action, cell: cell, path: indexPath, userInfo: userInfo)
|
||||
}
|
||||
|
||||
open override func responds(to selector: Selector) -> Bool {
|
||||
|
|
@ -82,36 +94,42 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
return scrollDelegate?.responds(to: selector) == true ? scrollDelegate : super.forwardingTarget(for: selector)
|
||||
}
|
||||
|
||||
// MARK: - Internal -
|
||||
// MARK: - Internal
|
||||
|
||||
func hasAction(_ action: TableRowActionType, atIndexPath indexPath: IndexPath) -> Bool {
|
||||
return sections[indexPath.section].rows[indexPath.row].hasAction(action)
|
||||
return sections[indexPath.section].rows[indexPath.row].has(action: action)
|
||||
}
|
||||
|
||||
func didReceiveAction(_ notification: Notification) {
|
||||
|
||||
guard let action = notification.object as? TableCellAction, let indexPath = tableView?.indexPath(for: action.cell) else { return }
|
||||
invoke(action: .custom(action.key), cell: action.cell, indexPath: indexPath)
|
||||
invoke(action: .custom(action.key), cell: action.cell, indexPath: indexPath, userInfo: notification.userInfo)
|
||||
}
|
||||
|
||||
// MARK: - Height
|
||||
|
||||
open func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
|
||||
|
||||
let row = sections[indexPath.section].rows[indexPath.row]
|
||||
|
||||
cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier)
|
||||
|
||||
return row.estimatedHeight ?? heightStrategy?.estimatedHeight(row, path: indexPath) ?? UITableViewAutomaticDimension
|
||||
|
||||
if rowHeightCalculator != nil {
|
||||
cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier)
|
||||
}
|
||||
|
||||
return row.defaultHeight ?? row.estimatedHeight ?? rowHeightCalculator?.estimatedHeight(forRow: row, at: indexPath) ?? UITableViewAutomaticDimension
|
||||
}
|
||||
|
||||
open func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
|
||||
let row = sections[indexPath.section].rows[indexPath.row]
|
||||
|
||||
if rowHeightCalculator != nil {
|
||||
cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier)
|
||||
}
|
||||
|
||||
let rowHeight = invoke(action: .height, cell: nil, indexPath: indexPath) as? CGFloat
|
||||
|
||||
return rowHeight ?? row.defaultHeight ?? heightStrategy?.height(row, path: indexPath) ?? UITableViewAutomaticDimension
|
||||
return rowHeight ?? row.defaultHeight ?? rowHeightCalculator?.height(forRow: row, at: indexPath) ?? UITableViewAutomaticDimension
|
||||
}
|
||||
|
||||
// MARK: UITableViewDataSource - configuration
|
||||
|
|
@ -127,6 +145,9 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
open func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
|
||||
let row = sections[indexPath.section].rows[indexPath.row]
|
||||
|
||||
cellRegisterer?.register(cellType: row.cellType, forCellReuseIdentifier: row.reuseIdentifier)
|
||||
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: row.reuseIdentifier, for: indexPath)
|
||||
|
||||
if cell.frame.size.width != tableView.frame.size.width {
|
||||
|
|
@ -163,13 +184,39 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
open func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
||||
|
||||
let section = sections[section]
|
||||
return section.headerHeight ?? section.headerView?.frame.size.height ?? 0
|
||||
return section.headerHeight ?? section.headerView?.frame.size.height ?? UITableViewAutomaticDimension
|
||||
}
|
||||
|
||||
open func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
||||
|
||||
let section = sections[section]
|
||||
return section.footerHeight ?? section.footerView?.frame.size.height ?? 0
|
||||
return section.footerHeight ?? section.footerView?.frame.size.height ?? UITableViewAutomaticDimension
|
||||
}
|
||||
|
||||
// MARK: UITableViewDataSource - Index
|
||||
|
||||
public func sectionIndexTitles(for tableView: UITableView) -> [String]? {
|
||||
|
||||
var indexTitles = [String]()
|
||||
var indexTitlesIndexes = [Int]()
|
||||
sections.enumerated().forEach { index, section in
|
||||
|
||||
if let title = section.indexTitle {
|
||||
indexTitles.append(title)
|
||||
indexTitlesIndexes.append(index)
|
||||
}
|
||||
}
|
||||
if !indexTitles.isEmpty {
|
||||
|
||||
sectionsIndexTitlesIndexes = indexTitlesIndexes
|
||||
return indexTitles
|
||||
}
|
||||
sectionsIndexTitlesIndexes = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
public func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int {
|
||||
return sectionsIndexTitlesIndexes?[index] ?? 0
|
||||
}
|
||||
|
||||
// MARK: UITableViewDelegate - actions
|
||||
|
|
@ -205,7 +252,7 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
return indexPath
|
||||
}
|
||||
|
||||
// MARK: - Row editing -
|
||||
// MARK: - Row editing
|
||||
|
||||
open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
|
||||
return sections[indexPath.section].rows[indexPath.row].isEditingAllowed(forIndexPath: indexPath)
|
||||
|
|
@ -221,8 +268,10 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
invoke(action: .clickDelete, cell: tableView.cellForRow(at: indexPath), indexPath: indexPath)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sections manipulation -
|
||||
}
|
||||
|
||||
// MARK: - Sections manipulation
|
||||
extension TableDirector {
|
||||
|
||||
@discardableResult
|
||||
open func append(section: TableSection) -> Self {
|
||||
|
|
@ -253,16 +302,42 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
}
|
||||
|
||||
@discardableResult
|
||||
open func delete(index: Int) -> Self {
|
||||
open func replaceSection(at index: Int, with section: TableSection) -> Self {
|
||||
|
||||
if index < sections.count {
|
||||
sections[index] = section
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
open func delete(sectionAt index: Int) -> Self {
|
||||
|
||||
sections.remove(at: index)
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
open func remove(sectionAt index: Int) -> Self {
|
||||
return delete(sectionAt: index)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
open func clear() -> Self {
|
||||
|
||||
rowHeightCalculator?.invalidate()
|
||||
sections.removeAll()
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
// MARK: - deprecated methods
|
||||
|
||||
@available(*, deprecated, message: "Use 'delete(sectionAt:)' method instead")
|
||||
@discardableResult
|
||||
open func delete(index: Int) -> Self {
|
||||
|
||||
sections.remove(at: index)
|
||||
return self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
//
|
||||
// Copyright (c) 2015 Max Sokolov https://twitter.com/max_sokolov
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
|
||||
struct TableKitNotifications {
|
||||
static let CellAction = "TableKitNotificationsCellAction"
|
||||
}
|
||||
|
||||
public protocol RowConfigurable {
|
||||
|
||||
func configure(_ cell: UITableViewCell)
|
||||
}
|
||||
|
||||
public protocol RowActionable {
|
||||
|
||||
var editingActions: [UITableViewRowAction]? { get }
|
||||
func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool
|
||||
|
||||
func invoke(action: TableRowActionType, cell: UITableViewCell?, path: IndexPath, userInfo: [AnyHashable: Any]?) -> Any?
|
||||
func has(action: TableRowActionType) -> Bool
|
||||
}
|
||||
|
||||
public protocol RowHashable {
|
||||
|
||||
var hashValue: Int { get }
|
||||
}
|
||||
|
||||
public protocol Row: RowConfigurable, RowActionable, RowHashable {
|
||||
|
||||
var reuseIdentifier: String { get }
|
||||
var cellType: AnyClass { get }
|
||||
|
||||
var estimatedHeight: CGFloat? { get }
|
||||
var defaultHeight: CGFloat? { get }
|
||||
}
|
||||
|
||||
public enum TableRowActionType {
|
||||
|
||||
case click
|
||||
case clickDelete
|
||||
case select
|
||||
case deselect
|
||||
case willSelect
|
||||
case willDisplay
|
||||
case shouldHighlight
|
||||
case height
|
||||
case canEdit
|
||||
case configure
|
||||
case custom(String)
|
||||
|
||||
var key: String {
|
||||
|
||||
switch (self) {
|
||||
case .custom(let key):
|
||||
return key
|
||||
default:
|
||||
return "_\(self)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol RowHeightCalculator {
|
||||
|
||||
func height(forRow row: Row, at indexPath: IndexPath) -> CGFloat
|
||||
func estimatedHeight(forRow row: Row, at indexPath: IndexPath) -> CGFloat
|
||||
|
||||
func invalidate()
|
||||
}
|
||||
|
|
@ -20,26 +20,18 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public protocol CellHeightCalculatable {
|
||||
open class TablePrototypeCellHeightCalculator: RowHeightCalculator {
|
||||
|
||||
func height(_ row: Row, path: IndexPath) -> CGFloat
|
||||
func estimatedHeight(_ row: Row, path: IndexPath) -> CGFloat
|
||||
|
||||
func invalidate()
|
||||
}
|
||||
|
||||
open class PrototypeHeightStrategy: CellHeightCalculatable {
|
||||
|
||||
private weak var tableView: UITableView?
|
||||
private(set) weak var tableView: UITableView?
|
||||
private var prototypes = [String: UITableViewCell]()
|
||||
private var cachedHeights = [Int: CGFloat]()
|
||||
private var separatorHeight = 1 / UIScreen.main.scale
|
||||
|
||||
init(tableView: UITableView?) {
|
||||
public init(tableView: UITableView?) {
|
||||
self.tableView = tableView
|
||||
}
|
||||
|
||||
open func height(_ row: Row, path: IndexPath) -> CGFloat {
|
||||
open func height(forRow row: Row, at indexPath: IndexPath) -> CGFloat {
|
||||
|
||||
guard let tableView = tableView else { return 0 }
|
||||
|
||||
|
|
@ -58,6 +50,7 @@ open class PrototypeHeightStrategy: CellHeightCalculatable {
|
|||
|
||||
guard let cell = prototypeCell else { return 0 }
|
||||
|
||||
cell.prepareForReuse()
|
||||
row.configure(cell)
|
||||
|
||||
cell.bounds = CGRect(x: 0, y: 0, width: tableView.bounds.size.width, height: cell.bounds.height)
|
||||
|
|
@ -71,7 +64,7 @@ open class PrototypeHeightStrategy: CellHeightCalculatable {
|
|||
return height
|
||||
}
|
||||
|
||||
open func estimatedHeight(_ row: Row, path: IndexPath) -> CGFloat {
|
||||
open func estimatedHeight(forRow row: Row, at indexPath: IndexPath) -> CGFloat {
|
||||
|
||||
guard let tableView = tableView else { return 0 }
|
||||
|
||||
|
|
@ -20,38 +20,10 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public protocol RowConfigurable {
|
||||
|
||||
func configure(_ cell: UITableViewCell)
|
||||
}
|
||||
|
||||
public protocol RowActionable {
|
||||
|
||||
var editingActions: [UITableViewRowAction]? { get }
|
||||
func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool
|
||||
|
||||
func invoke(_ action: TableRowActionType, cell: UITableViewCell?, path: IndexPath) -> Any?
|
||||
func hasAction(_ action: TableRowActionType) -> Bool
|
||||
}
|
||||
|
||||
public protocol RowHashable {
|
||||
|
||||
var hashValue: Int { get }
|
||||
}
|
||||
|
||||
public protocol Row: RowConfigurable, RowActionable, RowHashable {
|
||||
|
||||
var reuseIdentifier: String { get }
|
||||
var cellType: AnyClass { get }
|
||||
|
||||
var estimatedHeight: CGFloat? { get }
|
||||
var defaultHeight: CGFloat? { get }
|
||||
}
|
||||
|
||||
open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableViewCell {
|
||||
|
||||
open let item: CellType.T
|
||||
private lazy var actions = [String: TableRowAction<CellType>]()
|
||||
private lazy var actions = [String: [TableRowAction<CellType>]]()
|
||||
private(set) open var editingActions: [UITableViewRowAction]?
|
||||
|
||||
open var hashValue: Int {
|
||||
|
|
@ -78,29 +50,32 @@ open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableView
|
|||
|
||||
self.item = item
|
||||
self.editingActions = editingActions
|
||||
actions?.forEach { self.actions[$0.type.key] = $0 }
|
||||
actions?.forEach { on($0) }
|
||||
}
|
||||
|
||||
// MARK: - RowConfigurable -
|
||||
|
||||
open func configure(_ cell: UITableViewCell) {
|
||||
|
||||
(cell as? CellType)?.configure(with: item)
|
||||
}
|
||||
|
||||
// MARK: - RowActionable -
|
||||
|
||||
open func invoke(_ action: TableRowActionType, cell: UITableViewCell?, path: IndexPath) -> Any? {
|
||||
return actions[action.key]?.invoke(item: item, cell: cell, path: path)
|
||||
open func invoke(action: TableRowActionType, cell: UITableViewCell?, path: IndexPath, userInfo: [AnyHashable: Any]? = nil) -> Any? {
|
||||
|
||||
return actions[action.key]?.flatMap({ $0.invokeActionOn(cell: cell, item: item, path: path, userInfo: userInfo) }).last
|
||||
}
|
||||
|
||||
open func hasAction(_ action: TableRowActionType) -> Bool {
|
||||
open func has(action: TableRowActionType) -> Bool {
|
||||
|
||||
return actions[action.key] != nil
|
||||
}
|
||||
|
||||
open func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool {
|
||||
|
||||
if actions[TableRowActionType.canEdit.key] != nil {
|
||||
return invoke(.canEdit, cell: nil, path: indexPath) as? Bool ?? false
|
||||
return invoke(action: .canEdit, cell: nil, path: indexPath) as? Bool ?? false
|
||||
}
|
||||
return editingActions?.isEmpty == false || actions[TableRowActionType.clickDelete.key] != nil
|
||||
}
|
||||
|
|
@ -108,16 +83,55 @@ open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableView
|
|||
// MARK: - actions -
|
||||
|
||||
@discardableResult
|
||||
open func action(_ action: TableRowAction<CellType>) -> Self {
|
||||
open func on(_ action: TableRowAction<CellType>) -> Self {
|
||||
|
||||
if actions[action.type.key] == nil {
|
||||
actions[action.type.key] = [TableRowAction<CellType>]()
|
||||
}
|
||||
actions[action.type.key]?.append(action)
|
||||
|
||||
actions[action.type.key] = action
|
||||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
open func on<T>(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> T) -> Self {
|
||||
|
||||
return on(TableRowAction<CellType>(type, handler: handler))
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
open func action<T>(_ type: TableRowActionType, handler: @escaping (_ data: TableRowActionData<CellType>) -> T) -> Self {
|
||||
open func on(_ key: String, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> ()) -> Self {
|
||||
|
||||
actions[type.key] = TableRowAction<CellType>(type, handler: handler)
|
||||
return self
|
||||
return on(TableRowAction<CellType>(.custom(key), handler: handler))
|
||||
}
|
||||
|
||||
open func removeAllActions() {
|
||||
|
||||
actions.removeAll()
|
||||
}
|
||||
|
||||
open func removeAction(forActionId actionId: String) {
|
||||
|
||||
for (key, value) in actions {
|
||||
if let actionIndex = value.index(where: { $0.id == actionId }) {
|
||||
actions[key]?.remove(at: actionIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - deprecated actions -
|
||||
|
||||
@available(*, deprecated, message: "Use 'on' method instead")
|
||||
@discardableResult
|
||||
open func action(_ action: TableRowAction<CellType>) -> Self {
|
||||
|
||||
return on(action)
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "Use 'on' method instead")
|
||||
@discardableResult
|
||||
open func action<T>(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> T) -> Self {
|
||||
|
||||
return on(TableRowAction<CellType>(type, handler: handler))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,32 +20,7 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
public enum TableRowActionType {
|
||||
|
||||
case click
|
||||
case clickDelete
|
||||
case select
|
||||
case deselect
|
||||
case willSelect
|
||||
case willDisplay
|
||||
case shouldHighlight
|
||||
case height
|
||||
case canEdit
|
||||
case configure
|
||||
case custom(String)
|
||||
|
||||
var key: String {
|
||||
|
||||
switch (self) {
|
||||
case .custom(let key):
|
||||
return key
|
||||
default:
|
||||
return "_\(self)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class TableRowActionData<CellType: ConfigurableCell> where CellType: UITableViewCell {
|
||||
open class TableRowActionOptions<CellType: ConfigurableCell> where CellType: UITableViewCell {
|
||||
|
||||
open let item: CellType.T
|
||||
open let cell: CellType?
|
||||
|
|
@ -63,38 +38,46 @@ open class TableRowActionData<CellType: ConfigurableCell> where CellType: UITabl
|
|||
|
||||
private enum TableRowActionHandler<CellType: ConfigurableCell> where CellType: UITableViewCell {
|
||||
|
||||
case voidAction((TableRowActionData<CellType>) -> Void)
|
||||
case action((TableRowActionData<CellType>) -> Any?)
|
||||
case voidAction((TableRowActionOptions<CellType>) -> Void)
|
||||
case action((TableRowActionOptions<CellType>) -> Any?)
|
||||
|
||||
func invoke(item: CellType.T, cell: UITableViewCell?, path: IndexPath) -> Any? {
|
||||
func invoke(withOptions options: TableRowActionOptions<CellType>) -> Any? {
|
||||
|
||||
switch self {
|
||||
case .voidAction(let handler):
|
||||
return handler(TableRowActionData(item: item, cell: cell as? CellType, path: path, userInfo: nil))
|
||||
return handler(options)
|
||||
case .action(let handler):
|
||||
return handler(TableRowActionData(item: item, cell: cell as? CellType, path: path, userInfo: nil))
|
||||
return handler(options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open class TableRowAction<CellType: ConfigurableCell> where CellType: UITableViewCell {
|
||||
|
||||
open var id: String?
|
||||
open let type: TableRowActionType
|
||||
private let handler: TableRowActionHandler<CellType>
|
||||
|
||||
public init(_ type: TableRowActionType, handler: @escaping (_ data: TableRowActionData<CellType>) -> Void) {
|
||||
public init(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> Void) {
|
||||
|
||||
self.type = type
|
||||
self.handler = .voidAction(handler)
|
||||
}
|
||||
|
||||
public init<T>(_ type: TableRowActionType, handler: @escaping (_ data: TableRowActionData<CellType>) -> T) {
|
||||
public init(_ key: String, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> Void) {
|
||||
|
||||
self.type = .custom(key)
|
||||
self.handler = .voidAction(handler)
|
||||
}
|
||||
|
||||
public init<T>(_ type: TableRowActionType, handler: @escaping (_ options: TableRowActionOptions<CellType>) -> T) {
|
||||
|
||||
self.type = type
|
||||
self.handler = .action(handler)
|
||||
}
|
||||
|
||||
func invoke(item: CellType.T, cell: UITableViewCell?, path: IndexPath) -> Any? {
|
||||
return handler.invoke(item: item, cell: cell, path: path)
|
||||
public func invokeActionOn(cell: UITableViewCell?, item: CellType.T, path: IndexPath, userInfo: [AnyHashable: Any]?) -> Any? {
|
||||
|
||||
return handler.invoke(withOptions: TableRowActionOptions(item: item, cell: cell as? CellType, path: path, userInfo: userInfo))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ open class TableSection {
|
|||
|
||||
open var headerTitle: String?
|
||||
open var footerTitle: String?
|
||||
open var indexTitle: String?
|
||||
|
||||
open var headerView: UIView?
|
||||
open var footerView: UIView?
|
||||
|
|
@ -88,6 +89,17 @@ open class TableSection {
|
|||
rows[index] = row
|
||||
}
|
||||
|
||||
open func delete(rowAt index: Int) {
|
||||
rows.remove(at: index)
|
||||
}
|
||||
|
||||
open func remove(rowAt index: Int) {
|
||||
rows.remove(at: index)
|
||||
}
|
||||
|
||||
// MARK: - deprecated methods -
|
||||
|
||||
@available(*, deprecated, message: "Use 'delete(rowAt:)' method instead")
|
||||
open func delete(index: Int) {
|
||||
rows.remove(at: index)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ Pod::Spec.new do |s|
|
|||
s.name = 'TableKit'
|
||||
s.module_name = 'TableKit'
|
||||
|
||||
s.version = '2.0.0'
|
||||
s.version = '2.4.0'
|
||||
|
||||
s.homepage = 'https://github.com/maxsokolov/TableKit'
|
||||
s.summary = 'Type-safe declarative table views with Swift.'
|
||||
|
|
@ -14,4 +14,4 @@ Pod::Spec.new do |s|
|
|||
|
||||
s.source_files = 'Sources/*.swift'
|
||||
s.source = { :git => 'https://github.com/maxsokolov/TableKit.git', :tag => s.version }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,8 +8,9 @@
|
|||
|
||||
/* Begin PBXBuildFile section */
|
||||
50CF6E6B1D6704FE004746FF /* TableCellRegisterer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50CF6E6A1D6704FE004746FF /* TableCellRegisterer.swift */; };
|
||||
50E858581DB153F500A9AA55 /* TableKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50E858571DB153F500A9AA55 /* TableKit.swift */; };
|
||||
DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */; };
|
||||
DA9EA7B01D0EC2C90021F650 /* HeightStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A71D0EC2C90021F650 /* HeightStrategy.swift */; };
|
||||
DA9EA7B01D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A71D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift */; };
|
||||
DA9EA7B11D0EC2C90021F650 /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A81D0EC2C90021F650 /* Operators.swift */; };
|
||||
DA9EA7B21D0EC2C90021F650 /* TableCellAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7A91D0EC2C90021F650 /* TableCellAction.swift */; };
|
||||
DA9EA7B31D0EC2C90021F650 /* TableDirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA9EA7AA1D0EC2C90021F650 /* TableDirector.swift */; };
|
||||
|
|
@ -32,9 +33,10 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
50CF6E6A1D6704FE004746FF /* TableCellRegisterer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableCellRegisterer.swift; sourceTree = "<group>"; };
|
||||
50E858571DB153F500A9AA55 /* TableKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableKit.swift; sourceTree = "<group>"; };
|
||||
DA9EA7561D0B679A0021F650 /* TableKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TableKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurableCell.swift; sourceTree = "<group>"; };
|
||||
DA9EA7A71D0EC2C90021F650 /* HeightStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeightStrategy.swift; sourceTree = "<group>"; };
|
||||
DA9EA7A71D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TablePrototypeCellHeightCalculator.swift; sourceTree = "<group>"; };
|
||||
DA9EA7A81D0EC2C90021F650 /* Operators.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operators.swift; sourceTree = "<group>"; };
|
||||
DA9EA7A91D0EC2C90021F650 /* TableCellAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableCellAction.swift; sourceTree = "<group>"; };
|
||||
DA9EA7AA1D0EC2C90021F650 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -88,15 +90,16 @@
|
|||
DA9EA7A51D0EC2B90021F650 /* Sources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
50E858571DB153F500A9AA55 /* TableKit.swift */,
|
||||
DA9EA7AA1D0EC2C90021F650 /* TableDirector.swift */,
|
||||
50CF6E6A1D6704FE004746FF /* TableCellRegisterer.swift */,
|
||||
DA9EA7AB1D0EC2C90021F650 /* TableRow.swift */,
|
||||
DA9EA7AC1D0EC2C90021F650 /* TableRowAction.swift */,
|
||||
DA9EA7AE1D0EC2C90021F650 /* TableSection.swift */,
|
||||
DA9EA7A91D0EC2C90021F650 /* TableCellAction.swift */,
|
||||
DA9EA7A71D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift */,
|
||||
DA9EA7A61D0EC2C90021F650 /* ConfigurableCell.swift */,
|
||||
DA9EA7A81D0EC2C90021F650 /* Operators.swift */,
|
||||
DA9EA7A71D0EC2C90021F650 /* HeightStrategy.swift */,
|
||||
);
|
||||
path = Sources;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -231,11 +234,12 @@
|
|||
DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */,
|
||||
DA9EA7B31D0EC2C90021F650 /* TableDirector.swift in Sources */,
|
||||
DA9EA7B71D0EC2C90021F650 /* TableSection.swift in Sources */,
|
||||
DA9EA7B01D0EC2C90021F650 /* HeightStrategy.swift in Sources */,
|
||||
DA9EA7B01D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift in Sources */,
|
||||
DA9EA7B51D0EC2C90021F650 /* TableRowAction.swift in Sources */,
|
||||
DA9EA7B21D0EC2C90021F650 /* TableCellAction.swift in Sources */,
|
||||
DA9EA7B11D0EC2C90021F650 /* Operators.swift in Sources */,
|
||||
DA9EA7B41D0EC2C90021F650 /* TableRow.swift in Sources */,
|
||||
50E858581DB153F500A9AA55 /* TableKit.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ class TabletTests: XCTestCase {
|
|||
let expectation = self.expectation(description: "cell action")
|
||||
|
||||
let row = TableRow<TestTableViewCell>(item: TestData(title: "title"))
|
||||
.action(TableRowAction(.custom(TestTableViewCellOptions.CellAction)) { (data) in
|
||||
.on(TableRowAction(.custom(TestTableViewCellOptions.CellAction)) { (data) in
|
||||
|
||||
XCTAssertNotNil(data.cell, "Action data should have a cell")
|
||||
|
||||
|
|
@ -190,4 +190,54 @@ class TabletTests: XCTestCase {
|
|||
|
||||
waitForExpectations(timeout: 1.0, handler: nil)
|
||||
}
|
||||
|
||||
func testReplaceSectionOnExistingIndex() {
|
||||
|
||||
let row1 = TableRow<TestTableViewCell>(item: TestData(title: "title1"))
|
||||
let row2 = TableRow<TestTableViewCell>(item: TestData(title: "title2"))
|
||||
|
||||
let section1 = TableSection(headerView: nil, footerView: nil, rows: nil)
|
||||
section1 += row1
|
||||
|
||||
let section2 = TableSection(headerView: nil, footerView: nil, rows: nil)
|
||||
section2 += row2
|
||||
|
||||
testController.tableDirector += section1
|
||||
testController.tableView.reloadData()
|
||||
|
||||
let cell = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
|
||||
XCTAssertTrue(cell?.textLabel?.text == "title1")
|
||||
|
||||
testController.tableDirector.replaceSection(at: 0, with: section2)
|
||||
testController.tableView.reloadData()
|
||||
|
||||
let cell1 = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
|
||||
XCTAssertTrue(cell1?.textLabel?.text == "title2")
|
||||
}
|
||||
|
||||
|
||||
func testReplaceSectionOnWrongIndex() {
|
||||
|
||||
let row1 = TableRow<TestTableViewCell>(item: TestData(title: "title1"))
|
||||
let row2 = TableRow<TestTableViewCell>(item: TestData(title: "title2"))
|
||||
|
||||
let section1 = TableSection(headerView: nil, footerView: nil, rows: nil)
|
||||
section1 += row1
|
||||
|
||||
let section2 = TableSection(headerView: nil, footerView: nil, rows: nil)
|
||||
section2 += row2
|
||||
|
||||
testController.tableDirector += section1
|
||||
testController.tableView.reloadData()
|
||||
|
||||
let cell = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
|
||||
XCTAssertTrue(cell?.textLabel?.text == "title1")
|
||||
|
||||
testController.tableDirector.replaceSection(at: 33, with: section2)
|
||||
testController.tableView.reloadData()
|
||||
|
||||
let cell1 = testController.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? TestTableViewCell
|
||||
XCTAssertTrue(cell1?.textLabel?.text == "title1")
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue