diff --git a/CHANGELOG.md b/CHANGELOG.md index 01468992..5ab368fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +### 1.33.0 + +- **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 - **Added**: `BaseInitializableWebView` with navigation and error handling api. diff --git a/LeadKit.podspec b/LeadKit.podspec index ea371335..badd9a00 100644 --- a/LeadKit.podspec +++ b/LeadKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "LeadKit" - s.version = "1.32.0" + s.version = "1.33.0" s.summary = "iOS framework with a bunch of tools for rapid development" s.homepage = "https://github.com/TouchInstinct/LeadKit" s.license = "Apache License, Version 2.0" diff --git a/TIAppleMapUtils/TIAppleMapUtils.podspec b/TIAppleMapUtils/TIAppleMapUtils.podspec index 07eae3e0..5bf8344d 100644 --- a/TIAppleMapUtils/TIAppleMapUtils.podspec +++ b/TIAppleMapUtils/TIAppleMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIAppleMapUtils' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Set of helpers for map objects clustering and interacting using Apple MapKit.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIAuth/TIAuth.podspec b/TIAuth/TIAuth.podspec index 459b7752..387253f9 100644 --- a/TIAuth/TIAuth.podspec +++ b/TIAuth/TIAuth.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIAuth' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Login, registration, confirmation and other related actions' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIEcommerce/TIEcommerce.podspec b/TIEcommerce/TIEcommerce.podspec index 4bf9f962..c752fd0c 100644 --- a/TIEcommerce/TIEcommerce.podspec +++ b/TIEcommerce/TIEcommerce.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIEcommerce' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Cart, products, promocodes, bonuses and other related actions' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIFoundationUtils/TIFoundationUtils.podspec b/TIFoundationUtils/TIFoundationUtils.podspec index 1c3bd68e..de560ec2 100644 --- a/TIFoundationUtils/TIFoundationUtils.podspec +++ b/TIFoundationUtils/TIFoundationUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIFoundationUtils' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Set of helpers for Foundation framework classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIGoogleMapUtils/TIGoogleMapUtils.podspec b/TIGoogleMapUtils/TIGoogleMapUtils.podspec index 27ad5162..e3deb218 100644 --- a/TIGoogleMapUtils/TIGoogleMapUtils.podspec +++ b/TIGoogleMapUtils/TIGoogleMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIGoogleMapUtils' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Set of helpers for map objects clustering and interacting using Google Maps SDK.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIKeychainUtils/TIKeychainUtils.podspec b/TIKeychainUtils/TIKeychainUtils.podspec index f42b2fcc..321365d7 100644 --- a/TIKeychainUtils/TIKeychainUtils.podspec +++ b/TIKeychainUtils/TIKeychainUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIKeychainUtils' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Set of helpers for Keychain classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TILogging/TILogging.podspec b/TILogging/TILogging.podspec index fd6113b7..b0376aac 100644 --- a/TILogging/TILogging.podspec +++ b/TILogging/TILogging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TILogging' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Logging API' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIMapUtils/TIMapUtils.podspec b/TIMapUtils/TIMapUtils.podspec index 20a655f5..b91c2ed7 100644 --- a/TIMapUtils/TIMapUtils.podspec +++ b/TIMapUtils/TIMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIMapUtils' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Set of helpers for map objects clustering and interacting.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIMoyaNetworking/TIMoyaNetworking.podspec b/TIMoyaNetworking/TIMoyaNetworking.podspec index 015f1600..d394f56f 100644 --- a/TIMoyaNetworking/TIMoyaNetworking.podspec +++ b/TIMoyaNetworking/TIMoyaNetworking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIMoyaNetworking' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Moya + Swagger network service.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TINetworking/TINetworking.podspec b/TINetworking/TINetworking.podspec index aff3e7b7..0aa168f9 100644 --- a/TINetworking/TINetworking.podspec +++ b/TINetworking/TINetworking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TINetworking' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Swagger-frendly networking layer helpers.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TINetworkingCache/TINetworkingCache.podspec b/TINetworkingCache/TINetworkingCache.podspec index 01cc0173..e9be3cd8 100644 --- a/TINetworkingCache/TINetworkingCache.podspec +++ b/TINetworkingCache/TINetworkingCache.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TINetworkingCache' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Caching results of EndpointRequests.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIPagination/TIPagination.podspec b/TIPagination/TIPagination.podspec index 873e449d..c8ff1597 100644 --- a/TIPagination/TIPagination.podspec +++ b/TIPagination/TIPagination.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIPagination' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Generic pagination component.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TISwiftUICore/TISwiftUICore.podspec b/TISwiftUICore/TISwiftUICore.podspec index d9daa05d..76ae3c2d 100644 --- a/TISwiftUICore/TISwiftUICore.podspec +++ b/TISwiftUICore/TISwiftUICore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUICore' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Core UI elements: protocols, views and helpers.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TISwiftUtils/TISwiftUtils.podspec b/TISwiftUtils/TISwiftUtils.podspec index 03c09634..f9b76474 100644 --- a/TISwiftUtils/TISwiftUtils.podspec +++ b/TISwiftUtils/TISwiftUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUtils' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Bunch of useful helpers for Swift development.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITableKitUtils/Sources/Extensions/Appearance/TableRow+AppearanceConfigurable.swift b/TITableKitUtils/Sources/Extensions/Appearance/TableRow+AppearanceConfigurable.swift new file mode 100644 index 00000000..bdedb3d6 --- /dev/null +++ b/TITableKitUtils/Sources/Extensions/Appearance/TableRow+AppearanceConfigurable.swift @@ -0,0 +1,46 @@ +// +// 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 TIUIKitCore + +extension TableRow: AppearanceConfigurable where CellType: AppearanceConfigurable { + private static var configureAppearanceActionId: String { + "TableRowConfigureAppearanceActionId" + } + + public func with(appearance: CellType.Appearance) -> Self { + configure(appearance: appearance) + return self + } + + public func configure(appearance: CellType.Appearance) { + removeAction(forActionId: Self.configureAppearanceActionId) + + let action = TableRowAction(.configure) { options in + options.cell?.configure(appearance: appearance) + } + + action.id = Self.configureAppearanceActionId + on(action) + } +} 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/TITableKitUtils/Sources/Separators/Extensions/ContainerTableViewCell+Configurable.swift b/TITableKitUtils/Sources/Separators/Extensions/ContainerTableViewCell+Configurable.swift new file mode 100644 index 00000000..64f618bf --- /dev/null +++ b/TITableKitUtils/Sources/Separators/Extensions/ContainerTableViewCell+Configurable.swift @@ -0,0 +1,31 @@ +// +// 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 + +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/TITableKitUtils/TITableKitUtils.podspec b/TITableKitUtils/TITableKitUtils.podspec index 5d075dad..73456a50 100644 --- a/TITableKitUtils/TITableKitUtils.podspec +++ b/TITableKitUtils/TITableKitUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITableKitUtils' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Set of helpers for TableKit classes.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITransitions/TITransitions.podspec b/TITransitions/TITransitions.podspec index f52f0f60..e20b753a 100644 --- a/TITransitions/TITransitions.podspec +++ b/TITransitions/TITransitions.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITransitions' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Set of custom transitions to present controller. ' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIElements/Sources/Appearance/UILabel+Appearance.swift b/TIUIElements/Sources/Appearance/UILabel+Appearance.swift new file mode 100644 index 00000000..db90cc1a --- /dev/null +++ b/TIUIElements/Sources/Appearance/UILabel+Appearance.swift @@ -0,0 +1,52 @@ +// +// 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 UILabel { + open class BaseAppearance: UIView.BaseAppearance { + public var textAttributes: BaseTextAttributes? + + public init(layout: Layout = .defaultLayout, + backgroundColor: UIColor = .clear, + roundedCorners: CACornerMask = [], + cornerRadius: CGFloat = .zero, + shadow: UIViewShadow? = nil, + textAttributes: BaseTextAttributes? = nil) { + + self.textAttributes = textAttributes + + super.init(layout: layout, + backgroundColor: backgroundColor, + roundedCorners: roundedCorners, + cornerRadius: cornerRadius, + shadow: shadow) + } + } + + public final class DefaultAppearance: BaseAppearance, WrappedViewAppearance { + public static var defaultAppearance: DefaultAppearance { + DefaultAppearance() + } + } +} diff --git a/TIUIElements/Sources/Separators/SeparatorConfiguration.swift b/TIUIElements/Sources/Appearance/UILabel+AppearanceConfigurable.swift similarity index 76% rename from TIUIElements/Sources/Separators/SeparatorConfiguration.swift rename to TIUIElements/Sources/Appearance/UILabel+AppearanceConfigurable.swift index 0a44231c..2c33326a 100644 --- a/TIUIElements/Sources/Separators/SeparatorConfiguration.swift +++ b/TIUIElements/Sources/Appearance/UILabel+AppearanceConfigurable.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,15 @@ // THE SOFTWARE. // +import TIUIKitCore import UIKit -public struct SeparatorConfiguration { +extension UILabel { + public func configureUILabel(appearance: BaseAppearance) { + appearance.textAttributes? + .configure(label: self, + with: attributedText?.string ?? text) - 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 + super.configureUIView(appearance: appearance) } } diff --git a/TIUIElements/Sources/Appearance/UIVIew+AppearanceConfigurable.swift b/TIUIElements/Sources/Appearance/UIVIew+AppearanceConfigurable.swift new file mode 100644 index 00000000..624ca942 --- /dev/null +++ b/TIUIElements/Sources/Appearance/UIVIew+AppearanceConfigurable.swift @@ -0,0 +1,43 @@ +// +// 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 UIView { + public func configureUIView(appearance: BaseAppearance) { + backgroundColor = appearance.backgroundColor + layer.masksToBounds = true + layer.maskedCorners = appearance.roundedCorners + layer.cornerRadius = appearance.cornerRadius + + guard let shadow = appearance.shadow else { + return + } + + layer.shadowOpacity = shadow.opacity + layer.shadowOffset = shadow.offset + layer.shadowColor = shadow.color.cgColor + layer.shadowRadius = shadow.radius + clipsToBounds = false + } +} diff --git a/TIUIElements/Sources/Appearance/UIView+Appearance.swift b/TIUIElements/Sources/Appearance/UIView+Appearance.swift new file mode 100644 index 00000000..ceaa0d1b --- /dev/null +++ b/TIUIElements/Sources/Appearance/UIView+Appearance.swift @@ -0,0 +1,144 @@ +// +// 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 UIView { + + // MARK: - Layout Variations + + public struct NoLayout: ViewLayout { + public static var defaultLayout: Self { + Self() + } + } + + open class BaseSizeLayout { + public var size: CGSize + + public init(size: CGSize = .infinity) { + self.size = size + } + } + + public final class DefaultLayout: BaseSizeLayout, SizeViewLayout { + public static var defaultLayout: Self { + Self() + } + } + + // MARK: - WrappedView Layout + + open class BaseWrappedLayout: BaseSizeLayout { + public var centerOffset: UIOffset + public var insets: UIEdgeInsets + + public init(insets: UIEdgeInsets = .zero, + size: CGSize = .infinity, + centerOffset: UIOffset = .nan) { + + self.centerOffset = centerOffset + self.insets = insets + + super.init(size: size) + } + } + + public final class DefaultWrappedLayout: BaseWrappedLayout, WrappedViewLayout { + public static var defaultLayout: Self { + Self() + } + } + + // MARK: - Appearance Variations + + open class BaseAppearance { + public var layout: Layout + + public var backgroundColor: UIColor + public var roundedCorners: CACornerMask + public var cornerRadius: CGFloat + public var shadow: UIViewShadow? + + public init(layout: Layout = .defaultLayout, + backgroundColor: UIColor = .clear, + roundedCorners: CACornerMask = [], + cornerRadius: CGFloat = .zero, + shadow: UIViewShadow? = nil) { + + self.layout = layout + self.backgroundColor = backgroundColor + self.roundedCorners = roundedCorners + self.cornerRadius = cornerRadius + self.shadow = shadow + } + } + + public final class DefaultAppearance: BaseAppearance, ViewAppearance { + public static var defaultAppearance: Self { + Self() + } + } + + // MARK: - WrappedView Appearance + + open class BaseWrappedViewHolderAppearance: BaseAppearance { + + public var subviewAppearance: SubviewAppearance + + public init(layout: Layout = .defaultLayout, + backgroundColor: UIColor = .clear, + roundedCorners: CACornerMask = [], + cornerRadius: CGFloat = .zero, + shadow: UIViewShadow? = nil, + subviewAppearance: SubviewAppearance = .defaultAppearance) { + + self.subviewAppearance = subviewAppearance + + super.init(layout: layout, + backgroundColor: backgroundColor, + roundedCorners: roundedCorners, + cornerRadius: cornerRadius, + shadow: shadow) + } + } + + public final class DefaultWrappedViewHolderAppearance: BaseWrappedViewHolderAppearance, + WrappedViewHolderAppearance { + public static var defaultAppearance: Self { + Self() + } + } + + public final class DefaultWrappedAppearance: BaseAppearance, WrappedViewAppearance { + public static var defaultAppearance: Self { + Self() + } + } +} + +extension UIView.DefaultWrappedViewHolderAppearance: WrappedViewAppearance where Layout: WrappedViewLayout { + +} 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..ee4914f0 --- /dev/null +++ b/TIUIElements/Sources/Separators/SeparatorAppearance.swift @@ -0,0 +1,44 @@ +// +// 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 + +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..abf70b41 --- /dev/null +++ b/TIUIElements/Sources/Wrappers/Containers/CollectionTableViewCell.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 + +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 71% rename from TIUIElements/Sources/Wrappers/ContainerTableViewCell.swift rename to TIUIElements/Sources/Wrappers/Containers/ContainerTableViewCell.swift index 3892e327..585ed55c 100644 --- a/TIUIElements/Sources/Wrappers/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 new file mode 100644 index 00000000..77e07dd8 --- /dev/null +++ b/TIUIElements/Sources/Wrappers/Containers/ContainerView.swift @@ -0,0 +1,74 @@ +// +// 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 + +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() + } +} + +// 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/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/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 diff --git a/TIUIElements/TIUIElements.podspec b/TIUIElements/TIUIElements.podspec index 1d19c699..ec346e22 100644 --- a/TIUIElements/TIUIElements.podspec +++ b/TIUIElements/TIUIElements.podspec @@ -1,10 +1,11 @@ Pod::Spec.new do |s| s.name = 'TIUIElements' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Bunch of useful protocols and views.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } - s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' } + s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru', + 'castlele' => 'nikita.semenov@touchin.ru' } s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s } s.ios.deployment_target = '11.0' diff --git a/TIUIKitCore/Sources/Appearance/AppearanceConfigurable.swift b/TIUIKitCore/Sources/Appearance/AppearanceConfigurable.swift new file mode 100644 index 00000000..5c27df1b --- /dev/null +++ b/TIUIKitCore/Sources/Appearance/AppearanceConfigurable.swift @@ -0,0 +1,41 @@ +// +// 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 TISwiftUtils + +public protocol AppearanceConfigurable { + associatedtype Appearance: ViewAppearance + + func configure(appearance: Appearance) +} + +// MARK: - Creation methods + +extension AppearanceConfigurable { + @discardableResult + public func appearance(builder: ParameterClosure) -> Self { + let appearance = Appearance.defaultAppearance + builder(appearance) + configure(appearance: appearance) + return self + } +} diff --git a/TIUIKitCore/Sources/Appearance/UIViewShadow.swift b/TIUIKitCore/Sources/Appearance/UIViewShadow.swift new file mode 100644 index 00000000..3ea8addd --- /dev/null +++ b/TIUIKitCore/Sources/Appearance/UIViewShadow.swift @@ -0,0 +1,137 @@ +// +// 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 struct UIViewShadow { + public var radius: CGFloat + public var offset: CGSize + public var color: UIColor + public var opacity: Float + + public init(radius: CGFloat = .zero, + offset: CGSize = .zero, + color: UIColor = .clear, + opacity: Float = .zero) { + + self.radius = radius + self.offset = offset + self.color = color + self.opacity = opacity + } + + public init(@UIViewShadowBuilder builder: () -> [ShadowComponent]) { + let components: [ShadowComponent] = builder() + + var radius: CGFloat = .zero + var offset: CGSize = .zero + var color: UIColor = .clear + var opacity: Float = .zero + + for component in components { + switch component { + case let component as Radius: + radius = component.radius + + case let component as Offset: + offset = component.offset + + case let component as RGBA: + color = component.uiColor + + case let component as Color: + color = component.color + + case let component as Opacity: + opacity = component.opacity + + default: + continue + } + } + + if opacity == .zero, color != .clear { + opacity = Float(color.cgColor.alpha) + } + + self.init(radius: radius, offset: offset, color: color, opacity: opacity) + } +} + +// MARK: - UIViewShadowBuilder + +@resultBuilder +public struct UIViewShadowBuilder { + public static func buildBlock(_ components: ShadowComponent...) -> [ShadowComponent] { + components + } +} + +// MARK: - ShadowComponents + +public protocol ShadowComponent { + +} + +public struct Radius: ShadowComponent { + public let radius: CGFloat + + public init(_ radius: CGFloat) { + self.radius = radius + } +} + +public struct Offset: ShadowComponent { + public let offset: CGSize + + public init(_ offset: CGSize) { + self.offset = offset + } + + public init(_ x: CGFloat, _ y: CGFloat) { + self.offset = .init(width: x, height: y) + } +} + +public struct Color: ShadowComponent { + public let color: UIColor + + public init(_ color: UIColor) { + self.color = color + } +} + +public struct RGBA: ShadowComponent { + public let uiColor: UIColor + + public init(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat, _ a: CGFloat) { + self.uiColor = .init(red: r, green: g, blue: b, alpha: a) + } +} + +public struct Opacity: ShadowComponent { + public let opacity: Float + + public init(_ opacity: Float) { + self.opacity = opacity + } +} diff --git a/TIUIKitCore/Sources/Appearance/ViewAppearance.swift b/TIUIKitCore/Sources/Appearance/ViewAppearance.swift new file mode 100644 index 00000000..5c54f44d --- /dev/null +++ b/TIUIKitCore/Sources/Appearance/ViewAppearance.swift @@ -0,0 +1,73 @@ +// +// 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 ViewAppearance { + associatedtype Layout: ViewLayout + + static var defaultAppearance: Self { get } + + var layout: Layout { get } + var backgroundColor: UIColor { get } + var roundedCorners: CACornerMask { get } + var cornerRadius: CGFloat { get } + var shadow: UIViewShadow? { get } +} + +// MARK: - ViewAppearance Variations + +public protocol WrappedViewAppearance: ViewAppearance where Layout: WrappedViewLayout {} + +public protocol WrappedViewHolderAppearance: ViewAppearance { + associatedtype SubviewAppearance: WrappedViewAppearance + + var subviewAppearance: SubviewAppearance { get } +} + +// MARK: - Creation methods + +extension ViewAppearance { + public static func make(builder: (Self) -> Void) -> Self { + let appearance = Self.defaultAppearance + builder(appearance) + return appearance + } + + public static func callAsFunction(builder: (Self) -> Void) -> Self { + let appearance = Self.defaultAppearance + builder(appearance) + return appearance + } + + @discardableResult + public func update(builder: (Self) -> Void) -> Self { + builder(self) + return self + } + + @discardableResult + public func callAsFunction(builder: (Self) -> Void) -> Self { + builder(self) + return self + } +} diff --git a/TIUIKitCore/Sources/Appearance/ViewLayout.swift b/TIUIKitCore/Sources/Appearance/ViewLayout.swift new file mode 100644 index 00000000..82f89e0f --- /dev/null +++ b/TIUIKitCore/Sources/Appearance/ViewLayout.swift @@ -0,0 +1,55 @@ +// +// 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 TISwiftUtils +import UIKit + +public protocol ViewLayout { + static var defaultLayout: Self { get } +} + +// MARK: - ViewLayout Variations + +public protocol SizeViewLayout: ViewLayout { + var size: CGSize { get } +} + +public protocol WrappedViewLayout: SizeViewLayout { + var insets: UIEdgeInsets { get } + var centerOffset: UIOffset { get } +} + +// MARK: - Creation methods + +extension ViewLayout { + @discardableResult + public func update(builder: ParameterClosure) -> Self { + builder(self) + return self + } + + @discardableResult + public func callAsFunction(builder: ParameterClosure) -> Self { + builder(self) + return self + } +} diff --git a/TIUIKitCore/Sources/Extensions/CoreGraphics/CGSize+Extensions.swift b/TIUIKitCore/Sources/Extensions/CoreGraphics/CGSize+Extensions.swift new file mode 100644 index 00000000..7c05b946 --- /dev/null +++ b/TIUIKitCore/Sources/Extensions/CoreGraphics/CGSize+Extensions.swift @@ -0,0 +1,38 @@ +// +// 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 CoreGraphics.CGBase + +public extension CGSize { + static var infinity: Self { + Self(width: CGFloat.infinity, height: CGFloat.infinity) + } + + static func fixedWidth(_ width: CGFloat) -> Self { + Self(width: width, height: CGFloat.infinity) + } + + static func fixedHeight(_ height: CGFloat) -> Self { + Self(width: CGFloat.infinity, height: height) + } +} + diff --git a/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift b/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift new file mode 100644 index 00000000..8eb4257c --- /dev/null +++ b/TIUIKitCore/Sources/Extensions/UIKit/UIEdgeInsets+Extensions.swift @@ -0,0 +1,66 @@ +// +// 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 extension UIEdgeInsets { + + // MARK: - Factory methods + + static func edges(_ insets: CGFloat) -> UIEdgeInsets { + .init(top: insets, left: insets, bottom: insets, right: insets) + } + + static func horizontal(_ insets: CGFloat) -> UIEdgeInsets { + .init(top: .zero, left: insets, bottom: .zero, right: insets) + } + + static func vertical(_ insets: CGFloat) -> UIEdgeInsets { + .init(top: insets, left: .zero, bottom: insets, right: .zero) + } + + static func horizontal(left: CGFloat = .zero, right: CGFloat = .zero) -> UIEdgeInsets { + .init(top: .zero, left: left, bottom: .zero, right: right) + } + + static func vertical(top: CGFloat = .zero, bottom: CGFloat = .zero) -> UIEdgeInsets { + .init(top: top, left: .zero, bottom: bottom, right: .zero) + } + + // MARK: - Instance methods + + func horizontal(_ insets: CGFloat) -> UIEdgeInsets { + .init(top: top, left: insets, bottom: bottom, right: insets) + } + + func vertical(_ insets: CGFloat) -> UIEdgeInsets { + .init(top: insets, left: left, bottom: insets, right: right) + } + + func horizontal(left: CGFloat = .zero, right: CGFloat = .zero) -> UIEdgeInsets { + .init(top: top, left: left, bottom: bottom, right: right) + } + + func vertical(top: CGFloat = .zero, bottom: CGFloat = .zero) -> UIEdgeInsets { + .init(top: top, left: left, bottom: bottom, right: right) + } +} diff --git a/TIUIKitCore/Sources/Extensions/UIKit/UIOffset+Extensions.swift b/TIUIKitCore/Sources/Extensions/UIKit/UIOffset+Extensions.swift new file mode 100644 index 00000000..d1f6e938 --- /dev/null +++ b/TIUIKitCore/Sources/Extensions/UIKit/UIOffset+Extensions.swift @@ -0,0 +1,37 @@ +// +// 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 extension UIOffset { + static var nan: Self { + Self(horizontal: .nan, vertical: .nan) + } + + static func centerVertical(_ verticalOffset: CGFloat = .zero) -> Self { + Self(horizontal: .nan, vertical: verticalOffset) + } + + static func centerHorizontal(_ horizontalOffset: CGFloat = .zero) -> Self { + Self(horizontal: horizontalOffset, vertical: .nan) + } +} diff --git a/TIUIKitCore/TIUIKitCore.podspec b/TIUIKitCore/TIUIKitCore.podspec index 10099c4f..bdc83a95 100644 --- a/TIUIKitCore/TIUIKitCore.podspec +++ b/TIUIKitCore/TIUIKitCore.podspec @@ -1,10 +1,11 @@ Pod::Spec.new do |s| s.name = 'TIUIKitCore' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Core UI elements: protocols, views and helpers.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } - s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru' } + s.author = { 'petropavel13' => 'ivan.smolin@touchin.ru', + 'castlele' => 'nikita.semenov@touchin.ru' } s.source = { :git => 'https://github.com/TouchInstinct/LeadKit.git', :tag => s.version.to_s } s.ios.deployment_target = '11.0' diff --git a/TIWebView/TIWebView.podspec b/TIWebView/TIWebView.podspec index aca8bb71..2ac07c3d 100644 --- a/TIWebView/TIWebView.podspec +++ b/TIWebView/TIWebView.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIWebView' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Universal web view API' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIYandexMapUtils/TIYandexMapUtils.podspec b/TIYandexMapUtils/TIYandexMapUtils.podspec index e50f583f..755a8280 100644 --- a/TIYandexMapUtils/TIYandexMapUtils.podspec +++ b/TIYandexMapUtils/TIYandexMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIYandexMapUtils' - s.version = '1.32.0' + s.version = '1.33.0' s.summary = 'Set of helpers for map objects clustering and interacting using Yandex Maps SDK.' s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' }