builders to separate files

This commit is contained in:
Max Sokolov 2016-05-25 21:54:04 +03:00
parent 94925e301b
commit 051dba0e97
5 changed files with 263 additions and 208 deletions

View File

@ -0,0 +1,135 @@
//
// 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
public typealias ReturnValue = AnyObject?
/**
Responsible for building cells of given type and passing items to them.
*/
public class TableBaseRowBuilder<DataType, CellType where CellType: UITableViewCell> : RowBuilder {
public private(set) weak var tableDirector: TableDirector?
private var actions = [String: ActionHandler<DataType, CellType>]()
private var items = [DataType]()
public let reusableIdentifier: String
public var numberOfRows: Int {
return items.count
}
public init(item: DataType, id: String? = nil) {
reusableIdentifier = id ?? String(CellType)
items.append(item)
}
public init(items: [DataType]? = nil, id: String? = nil) {
reusableIdentifier = id ?? String(CellType)
if let items = items {
self.items.appendContentsOf(items)
}
}
public func rowHeight(index: Int) -> CGFloat {
return UITableViewAutomaticDimension
}
public func estimatedRowHeight() -> CGFloat {
return 44
}
// MARK: - Chaining actions -
public func action(key: String, handler: (data: ActionData<DataType, CellType>) -> Void) -> Self {
actions[key] = .Handler(handler)
return self
}
public func action(type: ActionType, handler: (data: ActionData<DataType, CellType>) -> Void) -> Self {
actions[type.key] = .Handler(handler)
return self
}
public func valueAction(type: ActionType, handler: (data: ActionData<DataType, CellType>) -> ReturnValue) -> Self {
actions[type.key] = .ValueHandler(handler)
return self
}
public func invoke(action action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
if let action = actions[action.key] {
return action.invoke(ActionData(cell: cell as? CellType, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex, userInfo: userInfo))
}
return nil
}
private func registerCell(inTableView tableView: UITableView) {
if tableView.dequeueReusableCellWithIdentifier(reusableIdentifier) != nil {
return
}
let resource = String(CellType)
let bundle = NSBundle(forClass: CellType.self)
if let _ = bundle.pathForResource(resource, ofType: "nib") { // existing cell
tableView.registerNib(UINib(nibName: resource, bundle: bundle), forCellReuseIdentifier: reusableIdentifier)
} else {
tableView.registerClass(CellType.self, forCellReuseIdentifier: reusableIdentifier)
}
}
public func willUpdateDirector(director: TableDirector?) {
tableDirector = director
}
// MARK: - Items manipulation -
public func item(index index: Int) -> DataType {
return items[index]
}
public func append(items items: [DataType]) {
self.items.appendContentsOf(items)
}
public func clear() {
items.removeAll()
}
}
public func +=<DataType, CellType>(left: TableBaseRowBuilder<DataType, CellType>, right: DataType) {
left.append(items: [right])
}
public func +=<DataType, CellType>(left: TableBaseRowBuilder<DataType, CellType>, right: [DataType]) {
left.append(items: right)
}

View File

@ -0,0 +1,112 @@
//
// 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
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 init(item: DataType) {
super.init(item: item, id: CellType.reusableIdentifier())
}
public init(items: [DataType]? = nil) {
super.init(items: items, id: CellType.reusableIdentifier())
}
public override func estimatedRowHeight() -> CGFloat {
return UITableViewAutomaticDimension
}
func heightCall(item: DataType, width: CGFloat) -> CGFloat {
guard let cell = prototypeCell else { return 0 }
cell.bounds = CGRectMake(0, 0, width, cell.bounds.height)
cell.configure(item)
cell.setNeedsLayout()
cell.layoutIfNeeded()
return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 1
}
// прехит по мере скроллинга в бэк
// прехит не должен прехитить то что уже есть (показанное)
// по мере скроллинга уметь отменять перхит ()
public override func rowHeight(index: Int) -> CGFloat {
guard let cell = prototypeCell else { return 0 }
let itemz = item(index: index)
if let height = cachedHeights[itemz.hashValue] {
return height
}
let height = heightCall(itemz, width: tableDirector?.tableView?.bounds.size.width ?? 0)
cachedHeights[itemz.hashValue] = height
print(tableDirector?.tableView?.bounds.size.width, cell.bounds.height, height)
return height
}
public func preheat(item: DataType) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let height = self.heightCall(item, width: 0)
// check if actual height exists
// calc height
//let heights = self.items.map { self.heightZ($0) }
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// check if table width is actual
// store height in cache
}
}
}
public override func invoke(action action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
if case .configure = action {
(cell as? CellType)?.configure(item(index: itemIndex))
}
return super.invoke(action: action, cell: cell, indexPath: indexPath, itemIndex: itemIndex, userInfo: userInfo)
}
public override func willUpdateDirector(director: TableDirector?) {
//tableDirector = director
if let tableView = director?.tableView, cell = tableView.dequeueReusableCellWithIdentifier(reusableIdentifier) as? CellType {
prototypeCell = cell
}
}
}

View File

@ -20,108 +20,6 @@
import UIKit
public typealias ReturnValue = AnyObject?
/**
Responsible for building cells of given type and passing items to them.
*/
public class TableBaseRowBuilder<DataType, CellType where CellType: UITableViewCell> : RowBuilder {
public private(set) weak var tableDirector: TableDirector?
private var actions = [String: ActionHandler<DataType, CellType>]()
private var items = [DataType]()
public let reusableIdentifier: String
public var numberOfRows: Int {
return items.count
}
public init(item: DataType, id: String? = nil) {
reusableIdentifier = id ?? String(CellType)
items.append(item)
}
public init(items: [DataType]? = nil, id: String? = nil) {
reusableIdentifier = id ?? String(CellType)
if let items = items {
self.items.appendContentsOf(items)
}
}
public func rowHeight(index: Int) -> CGFloat {
return UITableViewAutomaticDimension
}
public func estimatedRowHeight() -> CGFloat {
return 44
}
// MARK: - Chaining actions -
public func action(key: String, handler: (data: ActionData<DataType, CellType>) -> Void) -> Self {
actions[key] = .Handler(handler)
return self
}
public func action(type: ActionType, handler: (data: ActionData<DataType, CellType>) -> Void) -> Self {
actions[type.key] = .Handler(handler)
return self
}
public func valueAction(type: ActionType, handler: (data: ActionData<DataType, CellType>) -> ReturnValue) -> Self {
actions[type.key] = .ValueHandler(handler)
return self
}
public func invoke(action action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
if let action = actions[action.key] {
return action.invoke(ActionData(cell: cell as? CellType, indexPath: indexPath, item: items[itemIndex], itemIndex: itemIndex, userInfo: userInfo))
}
return nil
}
private func registerCell(inTableView tableView: UITableView) {
if tableView.dequeueReusableCellWithIdentifier(reusableIdentifier) != nil {
return
}
let resource = String(CellType)
let bundle = NSBundle(forClass: CellType.self)
if let _ = bundle.pathForResource(resource, ofType: "nib") { // existing cell
tableView.registerNib(UINib(nibName: resource, bundle: bundle), forCellReuseIdentifier: reusableIdentifier)
} else {
tableView.registerClass(CellType.self, forCellReuseIdentifier: reusableIdentifier)
}
}
public func willUpdateDirector(director: TableDirector?) {
tableDirector = director
}
// MARK: - Items manipulation -
public func append(items items: [DataType]) {
self.items.appendContentsOf(items)
}
public func clear() {
items.removeAll()
}
}
/**
Responsible for building configurable cells of given type and passing items to them.
*/
@ -130,118 +28,20 @@ public class TableRowBuilder<DataType, CellType: ConfigurableCell where CellType
public init(item: DataType) {
super.init(item: item, id: CellType.reusableIdentifier())
}
public init(items: [DataType]? = nil) {
super.init(items: items, id: CellType.reusableIdentifier())
}
public override func invoke(action action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
if case .configure = action {
(cell as? CellType)?.configure(items[itemIndex])
(cell as? CellType)?.configure(item(index: itemIndex))
}
return super.invoke(action: action, cell: cell, indexPath: indexPath, itemIndex: itemIndex, userInfo: userInfo)
}
public override func estimatedRowHeight() -> CGFloat {
return CGFloat(CellType.estimatedHeight())
}
}
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 init(item: DataType) {
super.init(item: item, id: CellType.reusableIdentifier())
}
public init(items: [DataType]? = nil) {
super.init(items: items, id: CellType.reusableIdentifier())
}
public override func estimatedRowHeight() -> CGFloat {
return UITableViewAutomaticDimension
}
func heightCall(item: DataType, width: CGFloat) -> CGFloat {
guard let cell = prototypeCell else { return 0 }
cell.bounds = CGRectMake(0, 0, width, cell.bounds.height)
cell.configure(item)
cell.setNeedsLayout()
cell.layoutIfNeeded()
return cell.contentView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize).height + 1
}
// прехит по мере скроллинга в бэк
// прехит не должен прехитить то что уже есть (показанное)
// по мере скроллинга уметь отменять перхит ()
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
}
let height = heightCall(item, width: tableDirector?.tableView?.bounds.size.width ?? 0)
cachedHeights[item.hashValue] = height
print(tableDirector?.tableView?.bounds.size.width, cell.bounds.height, height)
return height
}
public func preheat(item: DataType) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let height = self.heightCall(item, width: 0)
// check if actual height exists
// calc height
//let heights = self.items.map { self.heightZ($0) }
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// check if table width is actual
// store height in cache
}
}
}
public override func invoke(action action: ActionType, cell: UITableViewCell?, indexPath: NSIndexPath, itemIndex: Int, userInfo: [NSObject: AnyObject]?) -> AnyObject? {
if case .configure = action {
(cell as? CellType)?.configure(items[itemIndex])
}
return super.invoke(action: action, cell: cell, indexPath: indexPath, itemIndex: itemIndex, userInfo: userInfo)
}
public override func willUpdateDirector(director: TableDirector?) {
tableDirector = director
if let tableView = director?.tableView, cell = tableView.dequeueReusableCellWithIdentifier(reusableIdentifier) as? CellType {
prototypeCell = cell
}
}
}
public func +=<DataType, CellType>(left: TableBaseRowBuilder<DataType, CellType>, right: DataType) {
left.append(items: [right])
}
public func +=<DataType, CellType>(left: TableBaseRowBuilder<DataType, CellType>, right: [DataType]) {
left.append(items: right)
}

View File

@ -7,12 +7,14 @@
objects = {
/* Begin PBXBuildFile section */
5058386E1CF62B0700224C58 /* TableRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5058386D1CF62B0700224C58 /* TableRowBuilder.swift */; };
505838701CF62B1300224C58 /* TablePrototypeRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5058386F1CF62B1300224C58 /* 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 */; };
DAC2D6911C9D799E009E9C19 /* TableRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */; };
DAC2D6911C9D799E009E9C19 /* TableBaseRowBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68D1C9D799E009E9C19 /* TableBaseRowBuilder.swift */; };
DAC2D6921C9D799E009E9C19 /* TableSectionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */; };
DAC2D6931C9D799E009E9C19 /* Tablet.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D68F1C9D799E009E9C19 /* Tablet.swift */; };
DAC2D6991C9D7A3F009E9C19 /* TabletTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAC2D6961C9D7A3B009E9C19 /* TabletTests.swift */; };
@ -29,6 +31,8 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
5058386D1CF62B0700224C58 /* TableRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowBuilder.swift; sourceTree = "<group>"; };
5058386F1CF62B1300224C58 /* 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; };
@ -36,7 +40,7 @@
DAC2D6841C9D7517009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
DAC2D6851C9D7517009E9C19 /* Tablet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tablet.h; sourceTree = "<group>"; };
DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = "<group>"; };
DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowBuilder.swift; sourceTree = "<group>"; };
DAC2D68D1C9D799E009E9C19 /* TableBaseRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableBaseRowBuilder.swift; sourceTree = "<group>"; };
DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSectionBuilder.swift; sourceTree = "<group>"; };
DAC2D68F1C9D799E009E9C19 /* Tablet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tablet.swift; sourceTree = "<group>"; };
DAC2D6951C9D7A3B009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -96,7 +100,9 @@
DA08A04E1CF3AB0C00BBF1F8 /* ConfigurableCell.swift */,
DA08A0501CF3AB6100BBF1F8 /* RowBuilder.swift */,
DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */,
DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */,
DAC2D68D1C9D799E009E9C19 /* TableBaseRowBuilder.swift */,
5058386D1CF62B0700224C58 /* TableRowBuilder.swift */,
5058386F1CF62B1300224C58 /* TablePrototypeRowBuilder.swift */,
DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */,
DAC2D68F1C9D799E009E9C19 /* Tablet.swift */,
);
@ -225,7 +231,9 @@
DAC2D6921C9D799E009E9C19 /* TableSectionBuilder.swift in Sources */,
DA08A0511CF3AB6100BBF1F8 /* RowBuilder.swift in Sources */,
DA08A04F1CF3AB0C00BBF1F8 /* ConfigurableCell.swift in Sources */,
DAC2D6911C9D799E009E9C19 /* TableRowBuilder.swift in Sources */,
DAC2D6911C9D799E009E9C19 /* TableBaseRowBuilder.swift in Sources */,
5058386E1CF62B0700224C58 /* TableRowBuilder.swift in Sources */,
505838701CF62B1300224C58 /* TablePrototypeRowBuilder.swift in Sources */,
DAC2D6931C9D799E009E9C19 /* Tablet.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;