add prototype based cell height row builder
This commit is contained in:
parent
0250b08302
commit
ba8aa05d8b
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// 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
|
||||
|
||||
/**
|
||||
If you want to delegate your cell configuration logic to cell itself (with your view model or even model) than
|
||||
just provide an implementation of this protocol for your cell. Enjoy safe-typisation.
|
||||
*/
|
||||
public protocol ConfigurableCell {
|
||||
|
||||
associatedtype T
|
||||
|
||||
static func reusableIdentifier() -> String
|
||||
static func estimatedHeight() -> Float
|
||||
static func defaultHeight() -> Float?
|
||||
func configure(_: T)
|
||||
}
|
||||
|
||||
public extension ConfigurableCell where Self: UITableViewCell {
|
||||
|
||||
static func reusableIdentifier() -> String {
|
||||
return String(self)
|
||||
}
|
||||
|
||||
static func defaultHeight() -> Float? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -19,9 +19,16 @@
|
|||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import UIKit
|
||||
import Foundation
|
||||
|
||||
public class TablePrototypeRowBuilder<DataType: Hashable, CellType: ConfigurableCell where CellType.T == DataType, CellType: UITableViewCell> : TableBaseRowBuilder<DataType, CellType> {
|
||||
|
||||
public protocol RowBuilder {
|
||||
|
||||
var reusableIdentifier: String { get }
|
||||
var numberOfRows: Int { get }
|
||||
|
||||
var estimatedRowHeight: Float { get }
|
||||
|
||||
func rowHeight(index: Int) -> CGFloat
|
||||
|
||||
func invoke(action action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject?
|
||||
func registerCell(inTableView tableView: UITableView)
|
||||
}
|
||||
|
|
@ -146,7 +146,9 @@ public class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate
|
|||
}
|
||||
|
||||
public func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
|
||||
return invoke(action: .height, cell: nil, indexPath: indexPath) as? CGFloat ?? UITableViewAutomaticDimension
|
||||
|
||||
let builder = builderAtIndexPath(indexPath)
|
||||
return builder.0.rowHeight(builder.1)
|
||||
}
|
||||
|
||||
public func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
|
||||
|
|
|
|||
|
|
@ -23,35 +23,6 @@ import Foundation
|
|||
|
||||
public typealias ReturnValue = AnyObject?
|
||||
|
||||
enum ActionHandler<DataType, CellType> {
|
||||
|
||||
case Handler((data: ActionData<DataType, CellType>) -> Void)
|
||||
case ValueHandler((data: ActionData<DataType, CellType>) -> AnyObject?)
|
||||
|
||||
func invoke(data: ActionData<DataType, CellType>) -> ReturnValue {
|
||||
|
||||
switch (self) {
|
||||
case .Handler(let handler):
|
||||
handler(data: data)
|
||||
return nil
|
||||
case .ValueHandler(let handler):
|
||||
return handler(data: data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public protocol RowBuilder {
|
||||
|
||||
var reusableIdentifier: String { get }
|
||||
var numberOfRows: Int { get }
|
||||
|
||||
var rowHeight: Float { get }
|
||||
var estimatedRowHeight: Float { get }
|
||||
|
||||
func invoke(action action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject?
|
||||
func registerCell(inTableView tableView: UITableView)
|
||||
}
|
||||
|
||||
/**
|
||||
Responsible for building cells of given type and passing items to them.
|
||||
*/
|
||||
|
|
@ -70,10 +41,6 @@ public class TableBaseRowBuilder<DataType, CellType where CellType: UITableViewC
|
|||
return 44
|
||||
}
|
||||
|
||||
public var rowHeight: Float {
|
||||
return 0
|
||||
}
|
||||
|
||||
public init(item: DataType, id: String? = nil) {
|
||||
|
||||
reusableIdentifier = id ?? String(CellType)
|
||||
|
|
@ -89,6 +56,10 @@ public class TableBaseRowBuilder<DataType, CellType where CellType: UITableViewC
|
|||
}
|
||||
}
|
||||
|
||||
public func rowHeight(index: Int) -> CGFloat {
|
||||
return 0
|
||||
}
|
||||
|
||||
// MARK: - Chaining actions -
|
||||
|
||||
public func action(key: String, handler: (data: ActionData<DataType, CellType>) -> Void) -> Self {
|
||||
|
|
@ -170,6 +141,32 @@ public class TableRowBuilder<DataType, CellType: ConfigurableCell where CellType
|
|||
}
|
||||
}
|
||||
|
||||
public class TablePrototypeRowBuilder<DataType: Hashable, CellType: ConfigurableCell where CellType.T == DataType, CellType: UITableViewCell> : TableBaseRowBuilder<DataType, CellType> {
|
||||
|
||||
private var cachedHeights = [Int: CGFloat]()
|
||||
private var prototypeCell: CellType?
|
||||
|
||||
public override func rowHeight(index: Int) -> CGFloat {
|
||||
|
||||
guard let cell = prototypeCell else { return 0 }
|
||||
|
||||
let item = items[index]
|
||||
|
||||
if let height = cachedHeights[item.hashValue] {
|
||||
return height
|
||||
}
|
||||
|
||||
cell.configure(item)
|
||||
cell.setNeedsLayout()
|
||||
cell.layoutIfNeeded()
|
||||
|
||||
let height = cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 1
|
||||
cachedHeights[item.hashValue] = height
|
||||
|
||||
return height
|
||||
}
|
||||
}
|
||||
|
||||
public func +=<DataType, CellType>(left: TableBaseRowBuilder<DataType, CellType>, right: DataType) {
|
||||
left.append(items: [right])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,6 +69,23 @@ public class ActionData<DataType, CellType> {
|
|||
}
|
||||
}
|
||||
|
||||
enum ActionHandler<DataType, CellType> {
|
||||
|
||||
case Handler((data: ActionData<DataType, CellType>) -> Void)
|
||||
case ValueHandler((data: ActionData<DataType, CellType>) -> AnyObject?)
|
||||
|
||||
func invoke(data: ActionData<DataType, CellType>) -> ReturnValue {
|
||||
|
||||
switch (self) {
|
||||
case .Handler(let handler):
|
||||
handler(data: data)
|
||||
return nil
|
||||
case .ValueHandler(let handler):
|
||||
return handler(data: data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A custom action that you can trigger from your cell.
|
||||
You can eacily catch actions using a chaining manner with your row builder.
|
||||
|
|
@ -96,27 +113,3 @@ public class Action {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
If you want to delegate your cell configuration logic to cell itself (with your view model or even model) than
|
||||
just provide an implementation of this protocol for your cell. Enjoy safe-typisation.
|
||||
*/
|
||||
public protocol ConfigurableCell {
|
||||
|
||||
associatedtype T
|
||||
|
||||
static func reusableIdentifier() -> String
|
||||
static func estimatedHeight() -> Float
|
||||
static func defaultHeight() -> Float?
|
||||
func configure(_: T)
|
||||
}
|
||||
|
||||
public extension ConfigurableCell where Self: UITableViewCell {
|
||||
|
||||
static func reusableIdentifier() -> String {
|
||||
return String(self)
|
||||
}
|
||||
|
||||
static func defaultHeight() -> Float? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
501C70391CF387090099458A /* TablePrototypeRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 501C70381CF387090099458A /* TablePrototypeRowBuilder.swift */; };
|
||||
DA08A04F1CF3AB0C00BBF1F8 /* ConfigurableCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA08A04E1CF3AB0C00BBF1F8 /* ConfigurableCell.swift */; };
|
||||
DA08A0511CF3AB6100BBF1F8 /* RowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA08A0501CF3AB6100BBF1F8 /* RowBuilder.swift */; };
|
||||
DAC2D6741C9D743D009E9C19 /* Tablet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DAC2D6691C9D743D009E9C19 /* Tablet.framework */; };
|
||||
DAC2D6871C9D7517009E9C19 /* Tablet.h in Headers */ = {isa = PBXBuildFile; fileRef = DAC2D6851C9D7517009E9C19 /* Tablet.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DAC2D6901C9D799E009E9C19 /* TableDirector.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */; };
|
||||
|
|
@ -28,7 +29,8 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
501C70381CF387090099458A /* TablePrototypeRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TablePrototypeRowBuilder.swift; sourceTree = "<group>"; };
|
||||
DA08A04E1CF3AB0C00BBF1F8 /* ConfigurableCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurableCell.swift; sourceTree = "<group>"; };
|
||||
DA08A0501CF3AB6100BBF1F8 /* RowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowBuilder.swift; sourceTree = "<group>"; };
|
||||
DAC2D6691C9D743D009E9C19 /* Tablet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Tablet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DAC2D6731C9D743D009E9C19 /* TabletTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = TabletTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
DAC2D6841C9D7517009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
|
|
@ -91,11 +93,12 @@
|
|||
DAC2D68B1C9D7990009E9C19 /* Classes */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
DAC2D68F1C9D799E009E9C19 /* Tablet.swift */,
|
||||
DA08A04E1CF3AB0C00BBF1F8 /* ConfigurableCell.swift */,
|
||||
DA08A0501CF3AB6100BBF1F8 /* RowBuilder.swift */,
|
||||
DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */,
|
||||
DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */,
|
||||
DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */,
|
||||
501C70381CF387090099458A /* TablePrototypeRowBuilder.swift */,
|
||||
DAC2D68F1C9D799E009E9C19 /* Tablet.swift */,
|
||||
);
|
||||
name = Classes;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -220,8 +223,9 @@
|
|||
files = (
|
||||
DAC2D6901C9D799E009E9C19 /* TableDirector.swift in Sources */,
|
||||
DAC2D6921C9D799E009E9C19 /* TableSectionBuilder.swift in Sources */,
|
||||
DA08A0511CF3AB6100BBF1F8 /* RowBuilder.swift in Sources */,
|
||||
DA08A04F1CF3AB0C00BBF1F8 /* ConfigurableCell.swift in Sources */,
|
||||
DAC2D6911C9D799E009E9C19 /* TableRowBuilder.swift in Sources */,
|
||||
501C70391CF387090099458A /* TablePrototypeRowBuilder.swift in Sources */,
|
||||
DAC2D6931C9D799E009E9C19 /* Tablet.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
|||
Loading…
Reference in New Issue