diff --git a/Tablet.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate b/Tablet.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate index bb497d2..b016cad 100644 Binary files a/Tablet.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate and b/Tablet.xcworkspace/xcuserdata/max.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Tablet/TableBaseRowBuilder.swift b/Tablet/TableBaseRowBuilder.swift new file mode 100644 index 0000000..d31f092 --- /dev/null +++ b/Tablet/TableBaseRowBuilder.swift @@ -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 : RowBuilder { + + public private(set) weak var tableDirector: TableDirector? + + private var actions = [String: ActionHandler]() + 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) -> Void) -> Self { + + actions[key] = .Handler(handler) + return self + } + + public func action(type: ActionType, handler: (data: ActionData) -> Void) -> Self { + + actions[type.key] = .Handler(handler) + return self + } + + public func valueAction(type: ActionType, handler: (data: ActionData) -> 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 +=(left: TableBaseRowBuilder, right: DataType) { + left.append(items: [right]) +} + +public func +=(left: TableBaseRowBuilder, right: [DataType]) { + left.append(items: right) +} \ No newline at end of file diff --git a/Tablet/TablePrototypeRowBuilder.swift b/Tablet/TablePrototypeRowBuilder.swift new file mode 100644 index 0000000..b28cf68 --- /dev/null +++ b/Tablet/TablePrototypeRowBuilder.swift @@ -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 : TableBaseRowBuilder { + + 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 + } + } +} \ No newline at end of file diff --git a/Tablet/TableRowBuilder.swift b/Tablet/TableRowBuilder.swift index b101ddc..210a3e8 100644 --- a/Tablet/TableRowBuilder.swift +++ b/Tablet/TableRowBuilder.swift @@ -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 : RowBuilder { - - public private(set) weak var tableDirector: TableDirector? - - private var actions = [String: ActionHandler]() - 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) -> Void) -> Self { - - actions[key] = .Handler(handler) - return self - } - - public func action(type: ActionType, handler: (data: ActionData) -> Void) -> Self { - - actions[type.key] = .Handler(handler) - return self - } - - public func valueAction(type: ActionType, handler: (data: ActionData) -> 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 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 : TableBaseRowBuilder { - - 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 +=(left: TableBaseRowBuilder, right: DataType) { - left.append(items: [right]) -} - -public func +=(left: TableBaseRowBuilder, right: [DataType]) { - left.append(items: right) } \ No newline at end of file diff --git a/Tablet/Tablet.xcodeproj/project.pbxproj b/Tablet/Tablet.xcodeproj/project.pbxproj index ed7ad7e..77a0417 100644 --- a/Tablet/Tablet.xcodeproj/project.pbxproj +++ b/Tablet/Tablet.xcodeproj/project.pbxproj @@ -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 = ""; }; + 5058386F1CF62B1300224C58 /* TablePrototypeRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TablePrototypeRowBuilder.swift; sourceTree = ""; }; DA08A04E1CF3AB0C00BBF1F8 /* ConfigurableCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigurableCell.swift; sourceTree = ""; }; DA08A0501CF3AB6100BBF1F8 /* RowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RowBuilder.swift; sourceTree = ""; }; 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 = ""; }; DAC2D6851C9D7517009E9C19 /* Tablet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tablet.h; sourceTree = ""; }; DAC2D68C1C9D799E009E9C19 /* TableDirector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDirector.swift; sourceTree = ""; }; - DAC2D68D1C9D799E009E9C19 /* TableRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableRowBuilder.swift; sourceTree = ""; }; + DAC2D68D1C9D799E009E9C19 /* TableBaseRowBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableBaseRowBuilder.swift; sourceTree = ""; }; DAC2D68E1C9D799E009E9C19 /* TableSectionBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableSectionBuilder.swift; sourceTree = ""; }; DAC2D68F1C9D799E009E9C19 /* Tablet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tablet.swift; sourceTree = ""; }; DAC2D6951C9D7A3B009E9C19 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -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;