Compare commits

..

35 Commits

Author SHA1 Message Date
Ivan Smolin fec9537745 up swift version to 5.7 2024-01-30 23:09:39 +03:00
Ivan Smolin 246c0d06c0 Merge pull request 'remove outdated actions and swift tools version' (#2) from feature/remote_outdated_code into master
Reviewed-on: #2
2024-01-30 22:27:14 +03:00
Ivan Smolin 54bf141aff remove outdated actions and swift tools version 2024-01-30 22:24:27 +03:00
Ivan Smolin 01134b83b4 update source url 2024-01-30 22:13:02 +03:00
Ivan Smolin 3fefa09c3a Merge pull request 'fix warnings' (#1) from fix/xcode_15 into master
Reviewed-on: #1
2024-01-30 22:08:00 +03:00
Ivan Smolin 4886e43d20 fix warnings 2024-01-30 22:01:31 +03:00
Loupehope 41826e18db
Merge pull request #6 from TouchInstinct/feature/actionRows
Replace UITableViewRowAction with UIContextualAction
2020-12-26 15:37:30 +03:00
Vlad caec2dd10e Return deprecated 2020-12-26 15:22:00 +03:00
Vlad efc03d7c22 Add performsFirstActionWithFullSwipe 2020-12-26 11:24:59 +03:00
Vlad 94c40faa63 Code correction 2020-12-26 11:05:20 +03:00
Vlad d87a23587e Add backward capability to editingActions 2020-12-26 10:52:06 +03:00
Vlad 49b3f868f3 Return IPHONEOS_DEPLOYMENT_TARGET 2020-12-26 10:36:32 +03:00
Vlad 37482d3b69 Replace UITableViewRowAction with UIContextualAction 2020-12-26 10:31:38 +03:00
Loupehope 2cc161f0c0
Merge pull request #5 from maxsokolov/master
Merge source master
2020-12-26 10:16:07 +03:00
Ivan Zinovyev 1b4a988b35
Merge pull request #4 from TouchInstinct/expandable_states
Expandable states
2019-01-22 13:06:16 +03:00
Ivan Zinovyev f8f2ca6852 Fix indentation 2019-01-22 03:16:40 +03:00
Ivan Zinovyev ff18a4d8b8 Add Expandable States 2019-01-22 03:00:41 +03:00
Ivan Zinovyev d098691621 Merge remote-tracking branch 'maxsokolov/master' into expandable_states 2019-01-22 02:51:16 +03:00
Ivan Zinovyev 7f349c6944
Merge pull request #2 from TouchInstinct/feature/expandable
Feature/expandable
2019-01-09 19:13:15 +03:00
Ivan Zinovyev a768352b47 Some refactoring 2019-01-09 18:59:29 +03:00
Ivan Zinovyev 65cf31717b Remove deprecated attribute for estimatedHeight 2019-01-09 16:08:25 +03:00
Ivan Zinovyev 9d10bc18bf Remove manual height calculation functionality 2019-01-09 16:05:07 +03:00
Ivan Zinovyev 497b4e009c Make ExpandableCellHeightCalculator optional, not default 2019-01-09 16:04:32 +03:00
Ivan Zinovyev 6ef5ad504e Add initState for Expandable 2018-12-12 19:06:50 +03:00
Ivan Zinovyev e22ec03990 Add layout type 2018-12-12 19:03:16 +03:00
Ivan Zinovyev 5982d5db3a Use ExpandableCellHeightCalculator by default 2018-12-11 12:16:32 +03:00
Ivan Zinovyev 6eaf2cf3a2 Add Expandable protocol to expand/collapse cells 2018-12-10 17:15:43 +03:00
Ivan Zinovyev 921e2b42b3
Merge pull request #1 from iznv/height_for_item
Height for item
2018-11-27 12:30:04 +03:00
Ivan Zinovyev 70e6addcd0 Make AccurateCellHeightCalculator public 2018-11-04 08:31:46 +03:00
Ivan Zinovyev 86f07b8e7c Add AccurateCellHeightCalculator 2018-11-04 08:28:50 +03:00
Ivan Zinovyev 1c92c14a1a Fix for new swift 2018-11-04 08:20:28 +03:00
Ivan Zinovyev 7416271076 Fix type 2018-11-04 08:18:06 +03:00
Ivan Zinovyev 0812293813 Merge remote-tracking branch 'maxsokolov/master' into height_for_item 2018-11-04 08:16:15 +03:00
Ivan Zinovyev b62dea8702 Add default implementation for height(for:) 2018-04-22 01:59:58 +03:00
Ivan Zinovyev 279fdd4854 Add height for item 2018-04-22 01:50:04 +03:00
10 changed files with 181 additions and 68 deletions

View File

@ -1 +1 @@
5.0 5.7

View File

@ -1,5 +1,4 @@
// swift-tools-version:5.0 // swift-tools-version:5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription import PackageDescription

View File

@ -1,70 +1,96 @@
import UIKit import UIKit
public extension TimeInterval {
static let defaultExpandableAnimationDuration: TimeInterval = 0.3
}
public protocol Expandable { public protocol Expandable {
associatedtype ViewModelType: ExpandableCellViewModel associatedtype ViewModelType: ExpandableCellViewModel
var viewModel: ViewModelType? { get } var viewModel: ViewModelType? { get }
func configureAppearance(isCollapsed: Bool) func configure(state: ExpandableState)
} }
extension Expandable where Self: UITableViewCell & ConfigurableCell { extension Expandable where Self: UITableViewCell & ConfigurableCell {
public func initState() { public func initState() {
guard let viewModel = viewModel else { guard let viewModel = viewModel else {
return return
} }
changeState(isCollapsed: viewModel.isCollapsed) changeState(expandableState: viewModel.expandableState)
} }
private func changeState(isCollapsed: Bool) { private func changeState(expandableState: ExpandableState) {
// layout to get right frames, frame of bottom subview can be used to get expanded height // layout to get right frames, frame of bottom subview can be used to get expanded height
setNeedsLayout()
layoutIfNeeded() layoutIfNeeded()
// apply changes // apply changes
configureAppearance(isCollapsed: isCollapsed) configure(state: expandableState)
layoutIfNeeded() layoutIfNeeded()
} }
public func toggleState(animated: Bool = true, public func toggleState(animated: Bool = true,
animationDuration: TimeInterval = 0.3) { animationDuration: TimeInterval = .defaultExpandableAnimationDuration) {
guard let tableView = tableView, guard let viewModel = viewModel,
let viewModel = viewModel else { let stateIndex = viewModel.availableStates.firstIndex(where: { $0 == viewModel.expandableState }) else {
return return
} }
let targetState = stateIndex == viewModel.availableStates.count - 1
? viewModel.availableStates[0]
: viewModel.availableStates[stateIndex + 1]
transition(to: targetState,
animated: animated,
animationDuration: animationDuration)
}
public func transition(to state: ExpandableState,
animated: Bool = true,
animationDuration: TimeInterval = .defaultExpandableAnimationDuration) {
guard let tableView = tableView,
let viewModel = viewModel,
viewModel.expandableState != state else {
return
}
let contentOffset = tableView.contentOffset let contentOffset = tableView.contentOffset
if animated { if animated {
UIView.animate(withDuration: animationDuration, UIView.animate(withDuration: animationDuration,
animations: { [weak self] in animations: { [weak self] in
self?.applyChanges(isCollapsed: !viewModel.isCollapsed) self?.applyChanges(expandableState: state)
}, completion: { _ in }, completion: { _ in
viewModel.isCollapsed.toggle() viewModel.expandableState = state
}) })
} else { } else {
applyChanges(isCollapsed: !viewModel.isCollapsed) applyChanges(expandableState: state)
viewModel.isCollapsed.toggle() viewModel.expandableState = state
} }
tableView.beginUpdates() tableView.beginUpdates()
tableView.endUpdates() tableView.endUpdates()
tableView.setContentOffset(contentOffset, animated: false) tableView.setContentOffset(contentOffset, animated: false)
} }
private func applyChanges(isCollapsed: Bool) { public func applyChanges(expandableState: ExpandableState) {
changeState(isCollapsed: isCollapsed) changeState(expandableState: expandableState)
if let indexPath = indexPath, if let indexPath = indexPath,
let tableDirector = (tableView?.delegate as? TableDirector), let tableDirector = (tableView?.delegate as? TableDirector),
let cellHeightCalculator = tableDirector.rowHeightCalculator as? ExpandableCellHeightCalculator { let cellHeightCalculator = tableDirector.rowHeightCalculator as? ExpandableCellHeightCalculator {
cellHeightCalculator.updateCached(height: height(layoutType: Self.layoutType), for: indexPath) cellHeightCalculator.updateCached(height: expandableState.height ?? height(layoutType: Self.layoutType), for: indexPath)
} }
} }
} }

View File

@ -1,5 +1,15 @@
public protocol ExpandableCellViewModel: class { public protocol ExpandableCellViewModel: AnyObject {
var isCollapsed: Bool { get set } var expandableState: ExpandableState { get set }
var availableStates: [ExpandableState] { get }
}
public extension ExpandableCellViewModel {
var availableStates: [ExpandableState] {
return [.collapsed, .expanded]
}
} }

View File

@ -0,0 +1,41 @@
import UIKit
public enum ExpandableState {
case collapsed
case expanded
case height(value: CGFloat)
}
extension ExpandableState: Equatable { }
extension ExpandableState {
public var isCollapsed: Bool {
guard case .collapsed = self else {
return false
}
return true
}
public var isExpanded: Bool {
guard case .expanded = self else {
return false
}
return true
}
public var height: CGFloat? {
guard case let .height(value: height) = self else {
return nil
}
return height
}
}

View File

@ -346,9 +346,25 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool { open func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return sections[indexPath.section].rows[indexPath.row].isEditingAllowed(forIndexPath: indexPath) return sections[indexPath.section].rows[indexPath.row].isEditingAllowed(forIndexPath: indexPath)
} }
open func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? { open func tableView(_ tableView: UITableView,
return sections[indexPath.section].rows[indexPath.row].editingActions leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let currentRow = sections[indexPath.section].rows[indexPath.row]
let configuration = UISwipeActionsConfiguration(actions: currentRow.leadingContextualActions)
configuration.performsFirstActionWithFullSwipe = currentRow.performsFirstActionWithFullSwipe
return configuration
}
open func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let currentRow = sections[indexPath.section].rows[indexPath.row]
let configuration = UISwipeActionsConfiguration(actions: currentRow.trailingContextualActions)
configuration.performsFirstActionWithFullSwipe = currentRow.performsFirstActionWithFullSwipe
return configuration
} }
open func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle { open func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
@ -391,36 +407,36 @@ open class TableDirector: NSObject, UITableViewDataSource, UITableViewDelegate {
extension TableDirector { extension TableDirector {
@discardableResult @discardableResult
open func append(section: TableSection) -> Self { public func append(section: TableSection) -> Self {
append(sections: [section]) append(sections: [section])
return self return self
} }
@discardableResult @discardableResult
open func append(sections: [TableSection]) -> Self { public func append(sections: [TableSection]) -> Self {
self.sections.append(contentsOf: sections) self.sections.append(contentsOf: sections)
return self return self
} }
@discardableResult @discardableResult
open func append(rows: [Row]) -> Self { public func append(rows: [Row]) -> Self {
append(section: TableSection(rows: rows)) append(section: TableSection(rows: rows))
return self return self
} }
@discardableResult @discardableResult
open func insert(section: TableSection, atIndex index: Int) -> Self { public func insert(section: TableSection, atIndex index: Int) -> Self {
sections.insert(section, at: index) sections.insert(section, at: index)
return self return self
} }
@discardableResult @discardableResult
open func replaceSection(at index: Int, with section: TableSection) -> Self { public func replaceSection(at index: Int, with section: TableSection) -> Self {
if index < sections.count { if index < sections.count {
sections[index] = section sections[index] = section
} }
@ -428,20 +444,20 @@ extension TableDirector {
} }
@discardableResult @discardableResult
open func delete(sectionAt index: Int) -> Self { public func delete(sectionAt index: Int) -> Self {
sections.remove(at: index) sections.remove(at: index)
return self return self
} }
@discardableResult @discardableResult
open func remove(sectionAt index: Int) -> Self { public func remove(sectionAt index: Int) -> Self {
return delete(sectionAt: index) return delete(sectionAt: index)
} }
@discardableResult @discardableResult
open func clear() -> Self { public func clear() -> Self {
rowHeightCalculator?.invalidate() rowHeightCalculator?.invalidate()
sections.removeAll() sections.removeAll()
@ -451,7 +467,7 @@ extension TableDirector {
// MARK: - deprecated methods // MARK: - deprecated methods
@available(*, deprecated, message: "Use 'delete(sectionAt:)' method instead") @available(*, deprecated, message: "Use 'delete(sectionAt:)' method instead")
@discardableResult @discardableResult
open func delete(index: Int) -> Self { public func delete(index: Int) -> Self {
sections.remove(at: index) sections.remove(at: index)
return self return self

View File

@ -37,8 +37,10 @@ public protocol RowConfigurable {
} }
public protocol RowActionable { public protocol RowActionable {
var leadingContextualActions: [UIContextualAction] { get }
var editingActions: [UITableViewRowAction]? { get } var trailingContextualActions: [UIContextualAction] { get }
var performsFirstActionWithFullSwipe: Bool { get }
func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool func isEditingAllowed(forIndexPath indexPath: IndexPath) -> Bool
func invoke( func invoke(

View File

@ -21,10 +21,21 @@
import UIKit import UIKit
open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableViewCell { open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableViewCell {
public let item: CellType.CellData public let item: CellType.CellData
private lazy var actions = [String: [TableRowAction<CellType>]]() private lazy var actions = [String: [TableRowAction<CellType>]]()
private(set) open var editingActions: [UITableViewRowAction]?
open var leadingContextualActions: [UIContextualAction] {
[]
}
open var trailingContextualActions: [UIContextualAction] {
[]
}
open var performsFirstActionWithFullSwipe: Bool {
false
}
open var hashValue: Int { open var hashValue: Int {
return ObjectIdentifier(self).hashValue return ObjectIdentifier(self).hashValue
@ -50,10 +61,11 @@ open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableView
return CellType.self return CellType.self
} }
public init(item: CellType.CellData, actions: [TableRowAction<CellType>]? = nil, editingActions: [UITableViewRowAction]? = nil) { public init(item: CellType.CellData,
actions: [TableRowAction<CellType>]? = nil) {
self.item = item self.item = item
self.editingActions = editingActions
actions?.forEach { on($0) } actions?.forEach { on($0) }
} }
@ -81,7 +93,10 @@ open class TableRow<CellType: ConfigurableCell>: Row where CellType: UITableView
if actions[TableRowActionType.canEdit.key] != nil { if actions[TableRowActionType.canEdit.key] != nil {
return invoke(action: .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
return !leadingContextualActions.isEmpty
|| !trailingContextualActions.isEmpty
|| actions[TableRowActionType.clickDelete.key] != nil
} }
// MARK: - actions - // MARK: - actions -

View File

@ -2,16 +2,16 @@ Pod::Spec.new do |s|
s.name = 'TableKit' s.name = 'TableKit'
s.module_name = 'TableKit' s.module_name = 'TableKit'
s.version = '2.11.0' s.version = '2.12'
s.homepage = 'https://github.com/maxsokolov/TableKit' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/TableKit'
s.summary = 'Type-safe declarative table views with Swift.' s.summary = 'Type-safe declarative table views with Swift.'
s.author = { 'Max Sokolov' => 'i@maxsokolov.net' } s.author = { 'Max Sokolov' => 'i@maxsokolov.net' }
s.license = { :type => 'MIT', :file => 'LICENSE' } s.license = { :type => 'MIT', :file => 'LICENSE' }
s.platforms = { :ios => '8.0' } s.platforms = { :ios => '12.0' }
s.ios.deployment_target = '8.0' s.ios.deployment_target = '12.0'
s.source_files = 'Sources/*.swift' s.source_files = 'Sources/*.swift'
s.source = { :git => 'https://github.com/maxsokolov/TableKit.git', :tag => s.version } s.source = { :git => 'https://git.svc.touchin.ru/TouchInstinct/TableKit.git', :tag => s.version }
end end

View File

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
2CBFA2F521F692F100147B56 /* ExpandableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CBFA2F421F692F100147B56 /* ExpandableState.swift */; };
3201E78421BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78321BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift */; }; 3201E78421BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78321BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift */; };
3201E78621BE9E25001DF9E7 /* UITableViewCell+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78521BE9E25001DF9E7 /* UITableViewCell+Extensions.swift */; }; 3201E78621BE9E25001DF9E7 /* UITableViewCell+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78521BE9E25001DF9E7 /* UITableViewCell+Extensions.swift */; };
3201E78821BE9EB2001DF9E7 /* Expandable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78721BE9EB2001DF9E7 /* Expandable.swift */; }; 3201E78821BE9EB2001DF9E7 /* Expandable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3201E78721BE9EB2001DF9E7 /* Expandable.swift */; };
@ -37,6 +38,7 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
2CBFA2F421F692F100147B56 /* ExpandableState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableState.swift; sourceTree = "<group>"; };
3201E78321BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableCellHeightCalculator.swift; sourceTree = "<group>"; }; 3201E78321BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpandableCellHeightCalculator.swift; sourceTree = "<group>"; };
3201E78521BE9E25001DF9E7 /* UITableViewCell+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Extensions.swift"; sourceTree = "<group>"; }; 3201E78521BE9E25001DF9E7 /* UITableViewCell+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Extensions.swift"; sourceTree = "<group>"; };
3201E78721BE9EB2001DF9E7 /* Expandable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Expandable.swift; sourceTree = "<group>"; }; 3201E78721BE9EB2001DF9E7 /* Expandable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Expandable.swift; sourceTree = "<group>"; };
@ -115,6 +117,7 @@
3201E78721BE9EB2001DF9E7 /* Expandable.swift */, 3201E78721BE9EB2001DF9E7 /* Expandable.swift */,
3201E78921BE9ED4001DF9E7 /* ExpandableCellViewModel.swift */, 3201E78921BE9ED4001DF9E7 /* ExpandableCellViewModel.swift */,
32BDFE9E21C167F400D0BBB4 /* LayoutType.swift */, 32BDFE9E21C167F400D0BBB4 /* LayoutType.swift */,
2CBFA2F421F692F100147B56 /* ExpandableState.swift */,
); );
path = Sources; path = Sources;
sourceTree = "<group>"; sourceTree = "<group>";
@ -251,6 +254,7 @@
DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */, DA9EA7AF1D0EC2C90021F650 /* ConfigurableCell.swift in Sources */,
DA9EA7B31D0EC2C90021F650 /* TableDirector.swift in Sources */, DA9EA7B31D0EC2C90021F650 /* TableDirector.swift in Sources */,
3201E78821BE9EB2001DF9E7 /* Expandable.swift in Sources */, 3201E78821BE9EB2001DF9E7 /* Expandable.swift in Sources */,
2CBFA2F521F692F100147B56 /* ExpandableState.swift in Sources */,
DA9EA7B71D0EC2C90021F650 /* TableSection.swift in Sources */, DA9EA7B71D0EC2C90021F650 /* TableSection.swift in Sources */,
DA9EA7B01D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift in Sources */, DA9EA7B01D0EC2C90021F650 /* TablePrototypeCellHeightCalculator.swift in Sources */,
3201E78421BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift in Sources */, 3201E78421BE9DE1001DF9E7 /* ExpandableCellHeightCalculator.swift in Sources */,