Merge pull request #56 from maxsokolov/actions_improvements
Support multiple actions with same type
This commit is contained in:
commit
934e7623f9
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -2,8 +2,15 @@
|
|||
|
||||
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.1.0](https://github.com/maxsokolov/TableKit/releases/tag/1.4.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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
38
README.md
38
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.1.0-blue.svg" alt="CocoaPods compatible" /></a>
|
||||
<img src="https://img.shields.io/badge/platform-iOS-blue.svg?style=flat" alt="Platform iOS" />
|
||||
<a href="https://raw.githubusercontent.com/maxsokolov/tablekit/master/LICENSE"><img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="License: MIT" /></a>
|
||||
</p>
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -149,7 +169,7 @@ It's enough for most cases. But you may be not happy with this. So you could use
|
|||
```swift
|
||||
tableDirector.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 {
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -29,13 +29,15 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
open private(set) var sections = [TableSection]()
|
||||
|
||||
private weak var scrollDelegate: UIScrollViewDelegate?
|
||||
private var heightStrategy: CellHeightCalculatable?
|
||||
private var cellRegisterer: TableCellRegisterer?
|
||||
|
||||
public var rowHeightCalculator: RowHeightCalculator?
|
||||
|
||||
open var shouldUsePrototypeCellHeightCalculation: Bool = false {
|
||||
didSet {
|
||||
if shouldUsePrototypeCellHeightCalculation {
|
||||
heightStrategy = PrototypeHeightStrategy(tableView: tableView)
|
||||
rowHeightCalculator = TablePrototypeCellHeightCalculator(tableView: tableView)
|
||||
} else {
|
||||
rowHeightCalculator = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -50,7 +52,7 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
if shouldUseAutomaticCellRegistration {
|
||||
self.cellRegisterer = TableCellRegisterer(tableView: tableView)
|
||||
}
|
||||
|
||||
|
||||
self.scrollDelegate = scrollDelegate
|
||||
self.tableView = tableView
|
||||
self.tableView?.delegate = self
|
||||
|
|
@ -70,8 +72,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 {
|
||||
|
|
@ -85,33 +87,34 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
// 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
|
||||
return 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 shouldUsePrototypeCellHeightCalculation {
|
||||
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 +130,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 +169,13 @@ 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: UITableViewDelegate - actions
|
||||
|
|
@ -252,8 +258,7 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
return self
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
open func delete(index: Int) -> Self {
|
||||
open func delete(sectionAt index: Int) -> Self {
|
||||
|
||||
sections.remove(at: index)
|
||||
return self
|
||||
|
|
@ -262,7 +267,19 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
|
|||
@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 }
|
||||
|
||||
|
|
@ -71,7 +63,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,49 @@ 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))
|
||||
}
|
||||
|
||||
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<T>(_ type: TableRowActionType, handler: @escaping (_ data: TableRowActionData<CellType>) -> T) -> Self {
|
||||
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 {
|
||||
|
||||
actions[type.key] = TableRowAction<CellType>(type, handler: handler)
|
||||
return 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,48 @@ 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))
|
||||
handler(options)
|
||||
return nil
|
||||
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: Any, path: IndexPath, userInfo: [AnyHashable: Any]?) -> Any? {
|
||||
guard let item = item as? CellType.T else { return nil }
|
||||
|
||||
return handler.invoke(withOptions: TableRowActionOptions(item: item, cell: cell as? CellType, path: path, userInfo: userInfo))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,13 @@ open class TableSection {
|
|||
rows[index] = row
|
||||
}
|
||||
|
||||
open func delete(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.1.0'
|
||||
|
||||
s.homepage = 'https://github.com/maxsokolov/TableKit'
|
||||
s.summary = 'Type-safe declarative table views with Swift.'
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue