diff --git a/CHANGELOG.md b/CHANGELOG.md index 01468992..f259ab57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +### 1.32.1 + +- **Added**: `ViewAppearance` and `ViewLayout` models for setting up Views' appearance and layout +- **Added**: `TableKit.Row` extension for configuration inner View's appearance and layout + ### 1.32.0 - **Added**: `BaseInitializableWebView` with navigation and error handling api. diff --git a/LeadKit.podspec b/LeadKit.podspec index ea371335..4cc9067e 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.32.1" 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/Package.swift b/Package.swift index 63305190..b12322f9 100644 --- a/Package.swift +++ b/Package.swift @@ -63,7 +63,7 @@ let package = Package( // MARK: - Utils .target(name: "TISwiftUtils", path: "TISwiftUtils/Sources"), - .target(name: "TIFoundationUtils", dependencies: ["TISwiftUtils"], path: "TIFoundationUtils"), + .target(name: "TIFoundationUtils", dependencies: ["TISwiftUtils", "TIUIElements"], path: "TIFoundationUtils"), .target(name: "TIKeychainUtils", dependencies: ["TIFoundationUtils", "KeychainAccess"], path: "TIKeychainUtils/Sources"), .target(name: "TITableKitUtils", dependencies: ["TIUIElements", "TableKit"], path: "TITableKitUtils/Sources"), .target(name: "TILogging", dependencies: ["TIUIElements", "TISwiftUtils", "TIUIKitCore"], path: "TILogging/Sources"), diff --git a/TIAppleMapUtils/TIAppleMapUtils.podspec b/TIAppleMapUtils/TIAppleMapUtils.podspec index 07eae3e0..ca6cf589 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.32.1' 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..26e2b8cc 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.32.1' 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..98ecf75e 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.32.1' 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..c77c0a43 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.32.1' 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..1f16031a 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.32.1' 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..763913ae 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.32.1' 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..804149fe 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.32.1' 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..fcd04f76 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.32.1' 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..a82ebaf8 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.32.1' 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..32f04e84 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.32.1' 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..ae23d415 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.32.1' 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..8b04355a 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.32.1' 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..2b8fc405 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.32.1' 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..6918dd8c 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.32.1' 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..5430d7e9 --- /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" + } + + 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/TITableKitUtils.podspec b/TITableKitUtils/TITableKitUtils.podspec index 5d075dad..d72e9144 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.32.1' 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..9d714ec1 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.32.1' 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/TIUIElements.podspec b/TIUIElements/TIUIElements.podspec index 1d19c699..b22254f5 100644 --- a/TIUIElements/TIUIElements.podspec +++ b/TIUIElements/TIUIElements.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIElements' - s.version = '1.32.0' + s.version = '1.32.1' 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' } 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/Appearance/UILabel+Appearance.swift b/TIUIKitCore/Sources/Extensions/Appearance/UILabel+Appearance.swift new file mode 100644 index 00000000..9fc7fe95 --- /dev/null +++ b/TIUIKitCore/Sources/Extensions/Appearance/UILabel+Appearance.swift @@ -0,0 +1,51 @@ +// +// 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 + +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/TIUIKitCore/Sources/Extensions/Appearance/UILabel+AppearanceConfigurable.swift b/TIUIKitCore/Sources/Extensions/Appearance/UILabel+AppearanceConfigurable.swift new file mode 100644 index 00000000..14c5a0e6 --- /dev/null +++ b/TIUIKitCore/Sources/Extensions/Appearance/UILabel+AppearanceConfigurable.swift @@ -0,0 +1,33 @@ +// +// 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 + +extension UILabel { + public func configureUILabel(appearance: BaseAppearance) { + appearance.textAttributes? + .configure(label: self, + with: attributedText?.string ?? text) + + super.configureUIView(appearance: appearance) + } +} diff --git a/TIUIKitCore/Sources/Extensions/Appearance/UIVIew+AppearanceConfigurable.swift b/TIUIKitCore/Sources/Extensions/Appearance/UIVIew+AppearanceConfigurable.swift new file mode 100644 index 00000000..0a4cb89d --- /dev/null +++ b/TIUIKitCore/Sources/Extensions/Appearance/UIVIew+AppearanceConfigurable.swift @@ -0,0 +1,42 @@ +// +// 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 + +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/TIUIKitCore/Sources/Extensions/Appearance/UIView+Appearance.swift b/TIUIKitCore/Sources/Extensions/Appearance/UIView+Appearance.swift new file mode 100644 index 00000000..f450f5cc --- /dev/null +++ b/TIUIKitCore/Sources/Extensions/Appearance/UIView+Appearance.swift @@ -0,0 +1,143 @@ +// +// 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 + +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/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..7bf53b99 100644 --- a/TIUIKitCore/TIUIKitCore.podspec +++ b/TIUIKitCore/TIUIKitCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIKitCore' - s.version = '1.32.0' + s.version = '1.32.1' 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/TIWebView/TIWebView.podspec b/TIWebView/TIWebView.podspec index aca8bb71..48420452 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.32.1' 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..c2e3eda3 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.32.1' 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' }