From c5209dc9f65b7fe3c93649a1da940cedfdf17a37 Mon Sep 17 00:00:00 2001 From: Nikita Semenov Date: Fri, 10 Feb 2023 15:27:35 +0300 Subject: [PATCH 1/3] feat: container views --- .../WrappableView+TableViewContainers.swift | 35 ++++++++++++ .../Array/Array+SeparatorRowBox.swift | 34 +++++------ .../ContainerTableViewCell+Configurable.swift | 19 +++---- .../TableRow/TableRow+Separators.swift | 23 ++++---- .../Sources/Separators/SeparatorRowBox.swift | 10 ++-- ... => ContainerSeparatorTableViewCell.swift} | 57 ++++++++++--------- .../Separators/SeparatorAppearance.swift | 44 ++++++++++++++ .../Separators/SeparatorConfigurable.swift | 4 +- ...pe.swift => SeparatorsConfiguration.swift} | 22 +++---- .../Containers/CollectionTableViewCell.swift | 49 ++++++++++++++++ .../ContainerCollectionViewCell.swift | 0 .../ContainerTableViewCell.swift | 0 .../Wrappers/Containers/ContainerView.swift | 50 ++++++++++++++++ .../ReusableCollectionContainerView.swift | 0 ...ontainerTableViewCell+Configurations.swift | 40 +++++++++++++ .../ContainerView+Configurations.swift | 47 +++++++++++++++ .../Extensions/WrappableView+Containers.swift | 27 +++++++++ .../WrappedViewHolder+ConfigurableView.swift | 0 .../Wrappers/Protocols/WrappableView.swift | 27 +++++++++ .../{ => Protocols}/WrappedViewHolder.swift | 0 20 files changed, 399 insertions(+), 89 deletions(-) create mode 100644 TITableKitUtils/Sources/Extensions/Appearance/WrappableView+TableViewContainers.swift rename TIUIElements/Sources/Separators/SeparatorConfiguration.swift => TITableKitUtils/Sources/Separators/Extensions/ContainerTableViewCell+Configurable.swift (75%) rename TIUIElements/Sources/Separators/{BaseSeparatorCell.swift => ContainerSeparatorTableViewCell.swift} (71%) create mode 100644 TIUIElements/Sources/Separators/SeparatorAppearance.swift rename TIUIElements/Sources/Separators/{ViewSeparatorType.swift => SeparatorsConfiguration.swift} (79%) create mode 100644 TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift rename TIUIElements/Sources/Wrappers/{ => Containers}/ContainerCollectionViewCell.swift (100%) rename TIUIElements/Sources/Wrappers/{ => Containers}/ContainerTableViewCell.swift (100%) create mode 100644 TIUIElements/Sources/Wrappers/Containers/ContainerView.swift rename TIUIElements/Sources/Wrappers/{ => Containers}/ReusableCollectionContainerView.swift (100%) create mode 100644 TIUIElements/Sources/Wrappers/Extensions/ContainerTableViewCell+Configurations.swift create mode 100644 TIUIElements/Sources/Wrappers/Extensions/ContainerView+Configurations.swift create mode 100644 TIUIElements/Sources/Wrappers/Extensions/WrappableView+Containers.swift rename TIUIElements/Sources/Wrappers/{ => Extensions}/WrappedViewHolder+ConfigurableView.swift (100%) create mode 100644 TIUIElements/Sources/Wrappers/Protocols/WrappableView.swift rename TIUIElements/Sources/Wrappers/{ => Protocols}/WrappedViewHolder.swift (100%) diff --git a/TITableKitUtils/Sources/Extensions/Appearance/WrappableView+TableViewContainers.swift b/TITableKitUtils/Sources/Extensions/Appearance/WrappableView+TableViewContainers.swift new file mode 100644 index 00000000..80b170f2 --- /dev/null +++ b/TITableKitUtils/Sources/Extensions/Appearance/WrappableView+TableViewContainers.swift @@ -0,0 +1,35 @@ +// +// Copyright (c) 2023 Touch Instinct +// +// 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 TableKit +import TIUIElements +import TIUIKitCore +import UIKit + +public extension WrappableView where Self: ConfigurableView { + typealias InTableRow = TableKit.TableRow + typealias InSeparatableRow = TableKit.TableRow +} + +public extension WrappableView where Self: UITableViewCell & ConfigurableCell { + typealias TableRow = TableKit.TableRow +} diff --git a/TITableKitUtils/Sources/Separators/Extensions/Array/Array+SeparatorRowBox.swift b/TITableKitUtils/Sources/Separators/Extensions/Array/Array+SeparatorRowBox.swift index fa212ca2..623047a7 100644 --- a/TITableKitUtils/Sources/Separators/Extensions/Array/Array+SeparatorRowBox.swift +++ b/TITableKitUtils/Sources/Separators/Extensions/Array/Array+SeparatorRowBox.swift @@ -31,23 +31,23 @@ public extension Array where Element == SeparatorRowBox { } /// Configure separators from SeparatorRowBox array - /// - parameter extreme: Configuration that will be used for extreme values, for first or last row - /// - parameter middle: Configuration for intermediate rows - func configureSeparators(extreme extremeSeparatorConfiguration: SeparatorConfiguration, - middle middleSeparatorConfiguration: SeparatorConfiguration) { + /// - parameter extreme: Appearance that will be used for extreme values, for first or last row + /// - parameter middle: Apearance for intermediate rows + func configureSeparators(extreme extremeSeparatorsAppearance: SeparatorAppearance, + middle middleSeparatorsAppearance: SeparatorAppearance) { - configureSeparators(first: extremeSeparatorConfiguration, - middle: middleSeparatorConfiguration, - last: extremeSeparatorConfiguration) + configureSeparators(first: extremeSeparatorsAppearance, + middle: middleSeparatorsAppearance, + last: extremeSeparatorsAppearance) } /// Configure separators from SeparatorRowBox array - /// - parameter first: Configuration of the top separator of the first row - /// - parameter middle: Configuration of the separators between the rows - /// - parameter last: Configuration of the bottom separator of the last row - func configureSeparators(first firstSeparatorConfiguration: SeparatorConfiguration, - middle middleSeparatorConfiguration: SeparatorConfiguration, - last lastSeparatorConfiguration: SeparatorConfiguration) { + /// - parameter first: Appearance of the top separator of the first row + /// - parameter middle: Appearance of the separators between the rows + /// - parameter last: Appearance of the bottom separator of the last row + func configureSeparators(first firstSeparatorsAppearance: SeparatorAppearance, + middle middleSeparatorsAppearance: SeparatorAppearance, + last lastSeparatorsAppearance: SeparatorAppearance) { if isEmpty { return @@ -55,12 +55,12 @@ public extension Array where Element == SeparatorRowBox { switch count { case 1: - first?.set(separatorType: .full(firstSeparatorConfiguration, lastSeparatorConfiguration)) + first?.set(separatorType: .full(top: firstSeparatorsAppearance, bottom: lastSeparatorsAppearance)) default: - dropFirst().dropLast().forEach { $0.set(separatorType: .bottom(middleSeparatorConfiguration)) } - first?.set(separatorType: .full(firstSeparatorConfiguration, middleSeparatorConfiguration)) - last?.set(separatorType: .bottom(lastSeparatorConfiguration)) + dropFirst().dropLast().forEach { $0.set(separatorType: .bottom(middleSeparatorsAppearance)) } + first?.set(separatorType: .full(top: firstSeparatorsAppearance, bottom: middleSeparatorsAppearance)) + last?.set(separatorType: .bottom(lastSeparatorsAppearance)) } } } diff --git a/TIUIElements/Sources/Separators/SeparatorConfiguration.swift b/TITableKitUtils/Sources/Separators/Extensions/ContainerTableViewCell+Configurable.swift similarity index 75% rename from TIUIElements/Sources/Separators/SeparatorConfiguration.swift rename to TITableKitUtils/Sources/Separators/Extensions/ContainerTableViewCell+Configurable.swift index 0a44231c..64f618bf 100644 --- a/TIUIElements/Sources/Separators/SeparatorConfiguration.swift +++ b/TITableKitUtils/Sources/Separators/Extensions/ContainerTableViewCell+Configurable.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2020 Touch Instinct +// Copyright (c) 2023 Touch Instinct // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the Software), to deal @@ -20,17 +20,12 @@ // THE SOFTWARE. // -import UIKit +import TableKit +import TIUIElements +import TIUIKitCore -public struct SeparatorConfiguration { - - public let color: UIColor - public let insets: UIEdgeInsets - public let height: CGFloat - - public init(color: UIColor, insets: UIEdgeInsets = .zero, height: CGFloat = 1) { - self.color = color - self.insets = insets - self.height = height +extension ContainerTableViewCell: ConfigurableCell where View: ConfigurableView { + public func configure(with viewModel: View.ViewModelType) { + wrappedView.configure(with: viewModel) } } diff --git a/TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift b/TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift index 05ae4742..41a9ac58 100644 --- a/TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift +++ b/TITableKitUtils/Sources/Separators/Extensions/TableRow/TableRow+Separators.swift @@ -23,28 +23,29 @@ import TableKit import TIUIElements -private let configureSeparatorActionId = "TableRowConfigureSeparatorActionId" +extension TableRow: SeparatorsConfigurable where CellType: SeparatorsConfigurable { + private static var configureSeparatorsActionId: String { + "TableRowConfigureSeparatorsActionId" + } -public extension TableRow where CellType: SeparatorConfigurable { - - func with(separatorType: ViewSeparatorType) -> Self { - set(separatorType: separatorType) + public func with(separators: SeparatorsConfiguration) -> Self { + configureSeparators(with: separators) return self } - func set(separatorType: ViewSeparatorType) { - removeAction(forActionId: configureSeparatorActionId) + public func configureSeparators(with separatorsConfiguration: SeparatorsConfiguration) { + removeAction(forActionId: Self.configureSeparatorsActionId) - let action = TableRowAction(.configure) { - $0.cell?.configureSeparators(with: separatorType) + let action = TableRowAction(.configure) { options in + options.cell?.configureSeparators(with: separatorsConfiguration) } - action.id = configureSeparatorActionId + action.id = Self.configureSeparatorsActionId on(action) } } -public extension TableRow where CellType: SeparatorConfigurable { +public extension TableRow where CellType: SeparatorsConfigurable { /// TableRow typed as SeparatorRowBox var separatorRowBox: SeparatorRowBox { diff --git a/TITableKitUtils/Sources/Separators/SeparatorRowBox.swift b/TITableKitUtils/Sources/Separators/SeparatorRowBox.swift index 6109c4da..0c8f5cc7 100644 --- a/TITableKitUtils/Sources/Separators/SeparatorRowBox.swift +++ b/TITableKitUtils/Sources/Separators/SeparatorRowBox.swift @@ -21,21 +21,21 @@ // import TableKit -import TIUIElements import TISwiftUtils +import TIUIElements /// Class that used to configure separators when multiply cells presented in one section public final class SeparatorRowBox { - private let setSeparatorHandler: ParameterClosure + private let setSeparatorHandler: ParameterClosure - public func set(separatorType: ViewSeparatorType) { + public func set(separatorType: SeparatorsConfiguration) { setSeparatorHandler(separatorType) } public let row: Row - public init(row: TableRow) where T: SeparatorConfigurable { + public init(row: TableRow) where T: SeparatorsConfigurable { self.row = row - setSeparatorHandler = row.set + setSeparatorHandler = row.configureSeparators(with:) } } diff --git a/TIUIElements/Sources/Separators/BaseSeparatorCell.swift b/TIUIElements/Sources/Separators/ContainerSeparatorTableViewCell.swift similarity index 71% rename from TIUIElements/Sources/Separators/BaseSeparatorCell.swift rename to TIUIElements/Sources/Separators/ContainerSeparatorTableViewCell.swift index c426a46c..1f34f887 100644 --- a/TIUIElements/Sources/Separators/BaseSeparatorCell.swift +++ b/TIUIElements/Sources/Separators/ContainerSeparatorTableViewCell.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2020 Touch Instinct +// Copyright (c) 2023 Touch Instinct // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the Software), to deal @@ -22,7 +22,8 @@ import UIKit -open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable { +open class ContainerSeparatorTableViewCell: ContainerTableViewCell, SeparatorsConfigurable { + private lazy var topSeparatorView = createTopSeparator() private lazy var bottomSeparatorView = createBottomSeparator() @@ -52,23 +53,25 @@ open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable { contentView.addSubview(bottomSeparatorView) } - public func configureSeparators(with separatorType: ViewSeparatorType) { - topSeparatorView.isHidden = separatorType.topIsHidden - bottomSeparatorView.isHidden = separatorType.bottomIsHidden + // MARK: - SeparatorsConfigurable - switch separatorType { + public func configureSeparators(with separatorsConfiguration: SeparatorsConfiguration) { + topSeparatorView.isHidden = separatorsConfiguration.topIsHidden + bottomSeparatorView.isHidden = separatorsConfiguration.bottomIsHidden + + switch separatorsConfiguration { case .none: break - case let .bottom(configuration): - updateBottomSeparator(with: configuration) + case let .bottom(appearance): + updateBottomSeparator(with: appearance) - case let .top(configuration): - updateTopSeparator(with: configuration) + case let .top(appearance): + updateTopSeparator(with: appearance) - case let .full(topConfiguration, bottomConfiguration): - updateTopSeparator(with: topConfiguration) - updateBottomSeparator(with: bottomConfiguration) + case let .full(topAppearance, bottomAppearance): + updateTopSeparator(with: topAppearance) + updateBottomSeparator(with: bottomAppearance) } } @@ -127,26 +130,26 @@ open class BaseSeparatorCell: BaseInitializableCell, SeparatorConfigurable { $0.translatesAutoresizingMaskIntoConstraints = false } } -} -private extension BaseSeparatorCell { - func updateTopSeparator(with configuration: SeparatorConfiguration) { - topSeparatorView.backgroundColor = configuration.color + // MARK: - Private - topViewHeightConstraint?.constant = configuration.height + private func updateTopSeparator(with appearance: SeparatorAppearance) { + topSeparatorView.configureUIView(appearance: appearance) - topViewTopConstraint?.constant = configuration.insets.top - topViewLeftConstraint?.constant = configuration.insets.left - topViewRightConstraint?.constant = configuration.insets.right + topViewHeightConstraint?.constant = appearance.layout.size.height + + topViewTopConstraint?.constant = appearance.layout.insets.top + topViewLeftConstraint?.constant = appearance.layout.insets.left + topViewRightConstraint?.constant = appearance.layout.insets.right } - func updateBottomSeparator(with configuration: SeparatorConfiguration) { - bottomSeparatorView.backgroundColor = configuration.color + private func updateBottomSeparator(with appearance: SeparatorAppearance) { + bottomSeparatorView.configureUIView(appearance: appearance) - bottomViewHeightConstraint?.constant = configuration.height + bottomViewHeightConstraint?.constant = appearance.layout.size.height - bottomViewBottomConstraint?.constant = configuration.insets.bottom - bottomViewLeftConstraint?.constant = configuration.insets.left - bottomViewRightConstraint?.constant = configuration.insets.right + bottomViewBottomConstraint?.constant = appearance.layout.insets.bottom + bottomViewLeftConstraint?.constant = appearance.layout.insets.left + bottomViewRightConstraint?.constant = appearance.layout.insets.right } } diff --git a/TIUIElements/Sources/Separators/SeparatorAppearance.swift b/TIUIElements/Sources/Separators/SeparatorAppearance.swift new file mode 100644 index 00000000..e5dfec36 --- /dev/null +++ b/TIUIElements/Sources/Separators/SeparatorAppearance.swift @@ -0,0 +1,44 @@ +// +// Copyright (c) 2020 Touch Instinct +// +// 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 TIUIKitCore +import UIKit + +public final class SeparatorLayout: UIView.BaseWrappedLayout, WrappedViewLayout { + public static var defaultLayout: Self { + Self() + } + + public init(insets: UIEdgeInsets = .zero, + size: CGSize = .fixedHeight(0.5)) { + + super.init(insets: insets, + size: size, + centerOffset: .nan) + } +} + +public final class SeparatorAppearance: UIView.BaseAppearance, ViewAppearance { + public static var defaultAppearance: Self { + Self(backgroundColor: .lightGray) + } +} diff --git a/TIUIElements/Sources/Separators/SeparatorConfigurable.swift b/TIUIElements/Sources/Separators/SeparatorConfigurable.swift index 6e58a390..1bb3fb6a 100644 --- a/TIUIElements/Sources/Separators/SeparatorConfigurable.swift +++ b/TIUIElements/Sources/Separators/SeparatorConfigurable.swift @@ -20,6 +20,6 @@ // THE SOFTWARE. // -public protocol SeparatorConfigurable { - func configureSeparators(with separatorType: ViewSeparatorType) +public protocol SeparatorsConfigurable { + func configureSeparators(with separatorsConfiguration: SeparatorsConfiguration) } diff --git a/TIUIElements/Sources/Separators/ViewSeparatorType.swift b/TIUIElements/Sources/Separators/SeparatorsConfiguration.swift similarity index 79% rename from TIUIElements/Sources/Separators/ViewSeparatorType.swift rename to TIUIElements/Sources/Separators/SeparatorsConfiguration.swift index 9158ff2e..d312439b 100644 --- a/TIUIElements/Sources/Separators/ViewSeparatorType.swift +++ b/TIUIElements/Sources/Separators/SeparatorsConfiguration.swift @@ -20,22 +20,14 @@ // THE SOFTWARE. // -public enum ViewSeparatorType { - - /// All separators for view is hidden +public enum SeparatorsConfiguration { case none - - /// Show only top separator - case top(SeparatorConfiguration) - - /// Show only bottom separator - case bottom(SeparatorConfiguration) - - /// First configuration for top, second for bottom - case full(SeparatorConfiguration, SeparatorConfiguration) + case top(SeparatorAppearance) + case bottom(SeparatorAppearance) + case full(top: SeparatorAppearance, bottom: SeparatorAppearance) } -public extension ViewSeparatorType { +public extension SeparatorsConfiguration { /// Determine if bottom separator is hidden. var bottomIsHidden: Bool { @@ -48,7 +40,7 @@ public extension ViewSeparatorType { } /// Returns top configuration if type is top or full. - var topConfiguration: SeparatorConfiguration? { + var topConfiguration: SeparatorAppearance? { switch self { case let .top(configuration), let .full(configuration, _): return configuration @@ -59,7 +51,7 @@ public extension ViewSeparatorType { } /// Returns bottom configuration if type is bottom or full. - var bottomConfiguration: SeparatorConfiguration? { + var bottomConfiguration: SeparatorAppearance? { switch self { case let .bottom(configuration), let .full(_, configuration): return configuration diff --git a/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift b/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift new file mode 100644 index 00000000..927a58ea --- /dev/null +++ b/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift @@ -0,0 +1,49 @@ +// +// Copyright (c) 2023 Touch Instinct +// +// 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 + +open class CollectionTableViewCell: ContainerTableViewCell { + + // MARK: - UIView Overrides + + open override func systemLayoutSizeFitting(_ targetSize: CGSize, + withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, + verticalFittingPriority: UILayoutPriority) -> CGSize { + let cachedCollectionFrame = wrappedView.frame + wrappedView.frame.size.width = targetSize.width - contentInsets.left - contentInsets.right + let collectionContentHeight = wrappedView.collectionViewLayout.collectionViewContentSize.height + wrappedView.frame = cachedCollectionFrame + return CGSize(width: targetSize.width, + height: collectionContentHeight + contentInsets.top + contentInsets.bottom) + } + + // MARK: - Open methods + + open func createCollectionLayout() -> UICollectionViewLayout { + UICollectionViewFlowLayout() + } + + open override func createView() -> CollectionView { + CollectionView(frame: .zero, collectionViewLayout: createCollectionLayout()) + } +} diff --git a/TIUIElements/Sources/Wrappers/ContainerCollectionViewCell.swift b/TIUIElements/Sources/Wrappers/Containers/ContainerCollectionViewCell.swift similarity index 100% rename from TIUIElements/Sources/Wrappers/ContainerCollectionViewCell.swift rename to TIUIElements/Sources/Wrappers/Containers/ContainerCollectionViewCell.swift diff --git a/TIUIElements/Sources/Wrappers/ContainerTableViewCell.swift b/TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift similarity index 100% rename from TIUIElements/Sources/Wrappers/ContainerTableViewCell.swift rename to TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift diff --git a/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift b/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift new file mode 100644 index 00000000..77036515 --- /dev/null +++ b/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift @@ -0,0 +1,50 @@ +// +// Copyright (c) 2023 Touch Instinct +// +// 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 final class ContainerView: BaseInitializableView, WrappedViewHolder { + + public var wrappedView = View() + + public var contentInsets: UIEdgeInsets = .zero { + didSet { + contentEdgeConstraints?.update(from: contentInsets) + } + } + + private var contentEdgeConstraints: EdgeConstraints? + + // MARK: - InitializableView + + public override func addViews() { + super.addViews() + + addSubview(wrappedView) + } + + public override func configureLayout() { + super.configureLayout() + + contentEdgeConstraints = configureWrappedViewLayout() + } +} diff --git a/TIUIElements/Sources/Wrappers/ReusableCollectionContainerView.swift b/TIUIElements/Sources/Wrappers/Containers/ReusableCollectionContainerView.swift similarity index 100% rename from TIUIElements/Sources/Wrappers/ReusableCollectionContainerView.swift rename to TIUIElements/Sources/Wrappers/Containers/ReusableCollectionContainerView.swift diff --git a/TIUIElements/Sources/Wrappers/Extensions/ContainerTableViewCell+Configurations.swift b/TIUIElements/Sources/Wrappers/Extensions/ContainerTableViewCell+Configurations.swift new file mode 100644 index 00000000..4cb425ba --- /dev/null +++ b/TIUIElements/Sources/Wrappers/Extensions/ContainerTableViewCell+Configurations.swift @@ -0,0 +1,40 @@ +// +// Copyright (c) 2023 Touch Instinct +// +// 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 TIUIKitCore +import UIKit + +extension ContainerTableViewCell: AppearanceConfigurable where View: AppearanceConfigurable, + View.Appearance: WrappedViewAppearance { + + public func configure(appearance: DefaultWrappedViewHolderAppearance) { + configureContainerTableViewCell(appearance: appearance) + wrappedView.configure(appearance: appearance.subviewAppearance) + } +} + +extension ContainerTableViewCell { + public func configureContainerTableViewCell(appearance: BaseWrappedViewHolderAppearance) { + contentInsets = appearance.subviewAppearance.layout.insets + configureUIView(appearance: appearance) + } +} diff --git a/TIUIElements/Sources/Wrappers/Extensions/ContainerView+Configurations.swift b/TIUIElements/Sources/Wrappers/Extensions/ContainerView+Configurations.swift new file mode 100644 index 00000000..d35e0201 --- /dev/null +++ b/TIUIElements/Sources/Wrappers/Extensions/ContainerView+Configurations.swift @@ -0,0 +1,47 @@ +// +// Copyright (c) 2023 Touch Instinct +// +// 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 TIUIKitCore +import UIKit + +// MARK: - ConfigurableView + +extension ContainerView: ConfigurableView where View: ConfigurableView { + + public func configure(with viewModel: View.ViewModelType) { + wrappedView.configure(with: viewModel) + } +} + +// MARK: - AppearanceConfigurable + +extension ContainerView: AppearanceConfigurable where View: AppearanceConfigurable, + View.Appearance: WrappedViewAppearance { + + public typealias Appearance = UIView.DefaultWrappedViewHolderAppearance + + public func configure(appearance: Appearance) { + wrappedView.configure(appearance: appearance.subviewAppearance) + configureUIView(appearance: appearance) + contentInsets = appearance.subviewAppearance.layout.insets + } +} diff --git a/TIUIElements/Sources/Wrappers/Extensions/WrappableView+Containers.swift b/TIUIElements/Sources/Wrappers/Extensions/WrappableView+Containers.swift new file mode 100644 index 00000000..67dffcdc --- /dev/null +++ b/TIUIElements/Sources/Wrappers/Extensions/WrappableView+Containers.swift @@ -0,0 +1,27 @@ +// +// Copyright (c) 2023 Touch Instinct +// +// 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. +// + +public extension WrappableView { + typealias InContainerView = ContainerView + typealias InTableCell = ContainerTableViewCell + typealias InSeparatableTableCell = ContainerSeparatorTableViewCell +} diff --git a/TIUIElements/Sources/Wrappers/WrappedViewHolder+ConfigurableView.swift b/TIUIElements/Sources/Wrappers/Extensions/WrappedViewHolder+ConfigurableView.swift similarity index 100% rename from TIUIElements/Sources/Wrappers/WrappedViewHolder+ConfigurableView.swift rename to TIUIElements/Sources/Wrappers/Extensions/WrappedViewHolder+ConfigurableView.swift diff --git a/TIUIElements/Sources/Wrappers/Protocols/WrappableView.swift b/TIUIElements/Sources/Wrappers/Protocols/WrappableView.swift new file mode 100644 index 00000000..b8489dd7 --- /dev/null +++ b/TIUIElements/Sources/Wrappers/Protocols/WrappableView.swift @@ -0,0 +1,27 @@ +// +// Copyright (c) 2023 Touch Instinct +// +// 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 protocol WrappableView: UIView {} + +extension UIView: WrappableView {} diff --git a/TIUIElements/Sources/Wrappers/WrappedViewHolder.swift b/TIUIElements/Sources/Wrappers/Protocols/WrappedViewHolder.swift similarity index 100% rename from TIUIElements/Sources/Wrappers/WrappedViewHolder.swift rename to TIUIElements/Sources/Wrappers/Protocols/WrappedViewHolder.swift From 4e1270205e08b5c62d5e419d9edb6ec628bebae2 Mon Sep 17 00:00:00 2001 From: Nikita Semenov Date: Fri, 10 Feb 2023 16:28:53 +0300 Subject: [PATCH 2/3] docs: added change log information --- CHANGELOG.md | 3 +++ TIUIElements/Sources/Separators/SeparatorAppearance.swift | 2 +- .../Sources/Wrappers/Containers/CollectionTableViewCell.swift | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d70f9fc7..5ab368fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ - **Added**: `ViewAppearance` and `ViewLayout` models for setting up Views' appearance and layout - **Added**: `TableKit.Row` extension for configuration inner View's appearance and layout +- **Added**: `WrappableView` with typealiases for creating wrapped in the container views +- **Added**: `CollectionTableViewCell` and `ContainerView` +- **Update**: Separator appearance configureation for table views ### 1.32.0 diff --git a/TIUIElements/Sources/Separators/SeparatorAppearance.swift b/TIUIElements/Sources/Separators/SeparatorAppearance.swift index e5dfec36..ee4914f0 100644 --- a/TIUIElements/Sources/Separators/SeparatorAppearance.swift +++ b/TIUIElements/Sources/Separators/SeparatorAppearance.swift @@ -1,5 +1,5 @@ // -// Copyright (c) 2020 Touch Instinct +// Copyright (c) 2023 Touch Instinct // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the Software), to deal diff --git a/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift b/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift index 927a58ea..abf70b41 100644 --- a/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift +++ b/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.swift @@ -29,6 +29,7 @@ open class CollectionTableViewCell: ContainerT open override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize { + let cachedCollectionFrame = wrappedView.frame wrappedView.frame.size.width = targetSize.width - contentInsets.left - contentInsets.right let collectionContentHeight = wrappedView.collectionViewLayout.collectionViewContentSize.height From bd7d31cf679472a5eaf6ae0170b46314aaa0f12e Mon Sep 17 00:00:00 2001 From: Nikita Semenov Date: Fri, 10 Feb 2023 17:53:21 +0300 Subject: [PATCH 3/3] fix: code review notes --- .../Containers/ContainerTableViewCell.swift | 18 +++++++ .../Wrappers/Containers/ContainerView.swift | 24 ++++++++++ ...ontainerTableViewCell+Configurations.swift | 40 ---------------- .../ContainerView+Configurations.swift | 47 ------------------- 4 files changed, 42 insertions(+), 87 deletions(-) delete mode 100644 TIUIElements/Sources/Wrappers/Extensions/ContainerTableViewCell+Configurations.swift delete mode 100644 TIUIElements/Sources/Wrappers/Extensions/ContainerView+Configurations.swift diff --git a/TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift b/TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift index 3892e327..585ed55c 100644 --- a/TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift +++ b/TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift @@ -53,4 +53,22 @@ open class ContainerTableViewCell: BaseInitializableCell, WrappedV open func createView() -> View { return View() } + + // MARK: - Open methods + + public func configureContainerTableViewCell(appearance: BaseWrappedViewHolderAppearance) { + contentInsets = appearance.subviewAppearance.layout.insets + configureUIView(appearance: appearance) + } +} + +// MARK: - AppearanceConfigurable + +extension ContainerTableViewCell: AppearanceConfigurable where View: AppearanceConfigurable, + View.Appearance: WrappedViewAppearance { + + public func configure(appearance: DefaultWrappedViewHolderAppearance) { + configureContainerTableViewCell(appearance: appearance) + wrappedView.configure(appearance: appearance.subviewAppearance) + } } diff --git a/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift b/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift index 77036515..77e07dd8 100644 --- a/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift +++ b/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift @@ -20,6 +20,7 @@ // THE SOFTWARE. // +import TIUIKitCore import UIKit public final class ContainerView: BaseInitializableView, WrappedViewHolder { @@ -48,3 +49,26 @@ public final class ContainerView: BaseInitializableView, WrappedVi contentEdgeConstraints = configureWrappedViewLayout() } } + +// MARK: - ConfigurableView + +extension ContainerView: ConfigurableView where View: ConfigurableView { + + public func configure(with viewModel: View.ViewModelType) { + wrappedView.configure(with: viewModel) + } +} + +// MARK: - AppearanceConfigurable + +extension ContainerView: AppearanceConfigurable where View: AppearanceConfigurable, + View.Appearance: WrappedViewAppearance { + + public typealias Appearance = UIView.DefaultWrappedViewHolderAppearance + + public func configure(appearance: Appearance) { + wrappedView.configure(appearance: appearance.subviewAppearance) + configureUIView(appearance: appearance) + contentInsets = appearance.subviewAppearance.layout.insets + } +} diff --git a/TIUIElements/Sources/Wrappers/Extensions/ContainerTableViewCell+Configurations.swift b/TIUIElements/Sources/Wrappers/Extensions/ContainerTableViewCell+Configurations.swift deleted file mode 100644 index 4cb425ba..00000000 --- a/TIUIElements/Sources/Wrappers/Extensions/ContainerTableViewCell+Configurations.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// Copyright (c) 2023 Touch Instinct -// -// 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 TIUIKitCore -import UIKit - -extension ContainerTableViewCell: AppearanceConfigurable where View: AppearanceConfigurable, - View.Appearance: WrappedViewAppearance { - - public func configure(appearance: DefaultWrappedViewHolderAppearance) { - configureContainerTableViewCell(appearance: appearance) - wrappedView.configure(appearance: appearance.subviewAppearance) - } -} - -extension ContainerTableViewCell { - public func configureContainerTableViewCell(appearance: BaseWrappedViewHolderAppearance) { - contentInsets = appearance.subviewAppearance.layout.insets - configureUIView(appearance: appearance) - } -} diff --git a/TIUIElements/Sources/Wrappers/Extensions/ContainerView+Configurations.swift b/TIUIElements/Sources/Wrappers/Extensions/ContainerView+Configurations.swift deleted file mode 100644 index d35e0201..00000000 --- a/TIUIElements/Sources/Wrappers/Extensions/ContainerView+Configurations.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// Copyright (c) 2023 Touch Instinct -// -// 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 TIUIKitCore -import UIKit - -// MARK: - ConfigurableView - -extension ContainerView: ConfigurableView where View: ConfigurableView { - - public func configure(with viewModel: View.ViewModelType) { - wrappedView.configure(with: viewModel) - } -} - -// MARK: - AppearanceConfigurable - -extension ContainerView: AppearanceConfigurable where View: AppearanceConfigurable, - View.Appearance: WrappedViewAppearance { - - public typealias Appearance = UIView.DefaultWrappedViewHolderAppearance - - public func configure(appearance: Appearance) { - wrappedView.configure(appearance: appearance.subviewAppearance) - configureUIView(appearance: appearance) - contentInsets = appearance.subviewAppearance.layout.insets - } -}