From e8780d0238d33bb56f4dfcdc59529402457cba33 Mon Sep 17 00:00:00 2001 From: Ivan Smolin Date: Fri, 9 Feb 2024 16:11:13 +0300 Subject: [PATCH] Update: `ViewSkeletonsConfiguration`. It's possible to enable or disable animation for specific skeletons now. Added: `HolderViewSkeletonsConfiguration` for skeleton root view configuration Added: `DashedBoundsLayer` can now be applied to `CALayer` --- CHANGELOG.md | 5 ++ TIAppleMapUtils/TIAppleMapUtils.podspec | 2 +- TIApplication/TIApplication.podspec | 2 +- TIAuth/TIAuth.podspec | 2 +- TIBottomSheet/TIBottomSheet.podspec | 2 +- .../TICoreGraphicsUtils.podspec | 2 +- TIDeeplink/TIDeeplink.podspec | 2 +- .../DashedBoundsLayer/DashedBoundLayer.swift | 78 +++---------------- .../DashedBoundsLayer+CALayer.swift | 69 ++++++++++++++++ .../DashedBoundsLayer+UIView.swift | 71 +++++++++++++++++ .../DashedBoundsLayer+UIViewController.swift | 37 +++++++++ TIDeveloperUtils/TIDeveloperUtils.podspec | 2 +- TIEcommerce/TIEcommerce.podspec | 2 +- TIFoundationUtils/TIFoundationUtils.podspec | 2 +- TIGoogleMapUtils/TIGoogleMapUtils.podspec | 2 +- TIKeychainUtils/TIKeychainUtils.podspec | 2 +- TILogging/TILogging.podspec | 2 +- TIMapUtils/TIMapUtils.podspec | 2 +- TIMoyaNetworking/TIMoyaNetworking.podspec | 2 +- TINetworking/TINetworking.podspec | 2 +- TINetworkingCache/TINetworkingCache.podspec | 2 +- TIPagination/TIPagination.podspec | 2 +- TISwiftUICore/TISwiftUICore.podspec | 2 +- TISwiftUtils/TISwiftUtils.podspec | 2 +- TITableKitUtils/TITableKitUtils.podspec | 2 +- TITextProcessing/TITextProcessing.podspec | 2 +- .../BaseViewSkeletonsConfiguration.swift | 17 +++- .../ContainerViewSkeletonsConfiguration.swift | 20 +++-- .../HolderViewSkeletonsConfiguration.swift | 77 ++++++++++++++++++ .../SkeletonsConfiguration.swift | 40 ++++++---- .../TextSkeletonsConfiguration.swift | 25 +++--- .../Views/Skeletons/SkeletonLayer.swift | 63 ++++++++------- .../UIView+PresentingSkeletons.swift | 17 ++-- .../Skeletons.xcplaygroundpage/Contents.swift | 4 +- TIUIElements/TIUIElements.podspec | 2 +- TIUIKitCore/TIUIKitCore.podspec | 2 +- TIWebView/TIWebView.podspec | 2 +- TIYandexMapUtils/TIYandexMapUtils.podspec | 2 +- docs/tiuielements/skeletons.md | 4 +- 39 files changed, 410 insertions(+), 167 deletions(-) create mode 100644 TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+CALayer.swift create mode 100644 TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+UIView.swift create mode 100644 TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+UIViewController.swift create mode 100644 TIUIElements/Sources/Views/Skeletons/Configuration/HolderViewSkeletonsConfiguration.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index d4800f6f..b67a07e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +### 1.56.0 +- **Update**: `ViewSkeletonsConfiguration`. It's possible to enable or disable animation for specific skeletons now. +- **Added**: `HolderViewSkeletonsConfiguration` for skeleton root view configuration +- **Added**: `DashedBoundsLayer` can now be applied to `CALayer` + ### 1.55.1 - **Update**: revert `TextSkeletonsConfiguration` line height calculation diff --git a/TIAppleMapUtils/TIAppleMapUtils.podspec b/TIAppleMapUtils/TIAppleMapUtils.podspec index 6d346090..96a4420c 100644 --- a/TIAppleMapUtils/TIAppleMapUtils.podspec +++ b/TIAppleMapUtils/TIAppleMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIAppleMapUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Set of helpers for map objects clustering and interacting using Apple MapKit.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIApplication/TIApplication.podspec b/TIApplication/TIApplication.podspec index e568c68e..067009e9 100644 --- a/TIApplication/TIApplication.podspec +++ b/TIApplication/TIApplication.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIApplication' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Application architecture.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIAuth/TIAuth.podspec b/TIAuth/TIAuth.podspec index 91bf0fe8..a2a1d374 100644 --- a/TIAuth/TIAuth.podspec +++ b/TIAuth/TIAuth.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIAuth' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Login, registration, confirmation and other related actions' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIBottomSheet/TIBottomSheet.podspec b/TIBottomSheet/TIBottomSheet.podspec index db5789eb..569a2748 100644 --- a/TIBottomSheet/TIBottomSheet.podspec +++ b/TIBottomSheet/TIBottomSheet.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIBottomSheet' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Base models for creating bottom sheet view controllers' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TICoreGraphicsUtils/TICoreGraphicsUtils.podspec b/TICoreGraphicsUtils/TICoreGraphicsUtils.podspec index 0465b9c1..786e3c77 100644 --- a/TICoreGraphicsUtils/TICoreGraphicsUtils.podspec +++ b/TICoreGraphicsUtils/TICoreGraphicsUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TICoreGraphicsUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'CoreGraphics drawing helpers' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIDeeplink/TIDeeplink.podspec b/TIDeeplink/TIDeeplink.podspec index 94bef095..080f09dd 100644 --- a/TIDeeplink/TIDeeplink.podspec +++ b/TIDeeplink/TIDeeplink.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIDeeplink' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Deeplink service API' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundLayer.swift b/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundLayer.swift index cbc46486..5dcf45d0 100644 --- a/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundLayer.swift +++ b/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundLayer.swift @@ -35,82 +35,26 @@ open class DashedBoundsLayer: CAShapeLayer { // MARK: - Open methods open func configure(on view: UIView) { + configure(on: view.layer) + } + + open func configure(on layer: CALayer) { fillColor = UIColor.clear.cgColor strokeColor = dashColor.cgColor lineWidth = 1 lineDashPattern = [4.0, 2.0] - updateGeometry(from: view) + updateGeometry(newBounds: layer.bounds) - view.layer.addSublayer(self) + layer.addSublayer(self) - viewBoundsObservation = view.observe(\.bounds, options: [.new]) { [weak self] view, _ in - self?.updateGeometry(from: view) + viewBoundsObservation = layer.observe(\.bounds, options: [.new]) { [weak self] layer, _ in + self?.updateGeometry(newBounds: layer.bounds) } } - open func updateGeometry(from view: UIView) { - frame = view.bounds - path = UIBezierPath(rect: view.bounds).cgPath - } -} - -// MARK: - UIView + DashedBoundsLayer - -public extension UIView { - - @discardableResult - func debugBoundsVisually(debugSubviews: Bool = true, after delay: DispatchTimeInterval? = nil) -> UIView { - guard let delay else { - debugBoundsVisually(debugSubviews: debugSubviews) - return self - } - - DispatchQueue.main.asyncAfter(deadline: .now() + delay) { - self.debugBoundsVisually(debugSubviews: debugSubviews) - } - - return self - } - - @discardableResult - func debugBoundsVisually(debugSubviews: Bool = true) -> UIView { - disableBoundsVisuallyDebug() - - if debugSubviews { - for subview in subviews { - subview.debugBoundsVisually(debugSubviews: debugSubviews) - } - } - - let dashedLayer = DashedBoundsLayer() - dashedLayer.configure(on: self) - - return self - } - - func disableBoundsVisuallyDebug() { - for sublayer in layer.sublayers ?? [] { - if sublayer is DashedBoundsLayer { - sublayer.removeFromSuperlayer() - } - } - - for subview in subviews { - subview.disableBoundsVisuallyDebug() - } - } -} - -// MARK: - UIViewController + DashedBoundsLayer - -public extension UIViewController { - - @discardableResult - func debugBoundsVisually(debugSubviews: Bool = true, - after delay: DispatchTimeInterval? = nil) -> UIViewController { - - view.debugBoundsVisually(debugSubviews: debugSubviews, after: delay) - return self + open func updateGeometry(newBounds: CGRect) { + frame = newBounds + path = UIBezierPath(rect: newBounds).cgPath } } diff --git a/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+CALayer.swift b/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+CALayer.swift new file mode 100644 index 00000000..e2c05b52 --- /dev/null +++ b/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+CALayer.swift @@ -0,0 +1,69 @@ +// +// Copyright (c) 2024 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 CALayer { + + @discardableResult + func debugBoundsVisually(debugSublayers: Bool = true, + after delay: DispatchTimeInterval? = nil, + filterSublayers isIncluded: @escaping (CALayer) -> Bool = { _ in true }) -> Self { + guard let delay else { + debugBoundsVisually(debugSublayers: debugSublayers, filterSublayers: isIncluded) + return self + } + + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { + self.debugBoundsVisually(debugSublayers: debugSublayers, filterSublayers: isIncluded) + } + + return self + } + + @discardableResult + func debugBoundsVisually(debugSublayers: Bool = true, + filterSublayers isIncluded: (CALayer) -> Bool = { _ in true }) -> Self { + disableBoundsVisuallyDebug() + + if debugSublayers { + for sublayer in sublayers?.filter(isIncluded) ?? [] { + sublayer.debugBoundsVisually(debugSublayers: debugSublayers) + } + } + + let dashedLayer = DashedBoundsLayer() + dashedLayer.configure(on: self) + + return self + } + + func disableBoundsVisuallyDebug() { + for sublayer in sublayers ?? [] { + if sublayer is DashedBoundsLayer { + sublayer.removeFromSuperlayer() + } else { + sublayer.disableBoundsVisuallyDebug() + } + } + } +} diff --git a/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+UIView.swift b/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+UIView.swift new file mode 100644 index 00000000..2763e77a --- /dev/null +++ b/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+UIView.swift @@ -0,0 +1,71 @@ +// +// Copyright (c) 2024 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 UIView { + + @discardableResult + func debugBoundsVisually(debugSubviews: Bool = true, + after delay: DispatchTimeInterval? = nil, + filterSubviews isIncluded: @escaping (UIView) -> Bool = { _ in true }) -> Self { + guard let delay else { + debugBoundsVisually(debugSubviews: debugSubviews) + return self + } + + DispatchQueue.main.asyncAfter(deadline: .now() + delay) { + self.debugBoundsVisually(debugSubviews: debugSubviews) + } + + return self + } + + @discardableResult + func debugBoundsVisually(debugSubviews: Bool = true, + filterSubviews isIncluded: @escaping (UIView) -> Bool = { _ in true }) -> Self { + disableBoundsVisuallyDebug() + + if debugSubviews { + for subview in subviews.filter(isIncluded) { + subview.debugBoundsVisually(debugSubviews: debugSubviews) + } + } + + let dashedLayer = DashedBoundsLayer() + dashedLayer.configure(on: self) + + return self + } + + func disableBoundsVisuallyDebug() { + for sublayer in layer.sublayers ?? [] { + if sublayer is DashedBoundsLayer { + sublayer.removeFromSuperlayer() + } + } + + for subview in subviews { + subview.disableBoundsVisuallyDebug() + } + } +} diff --git a/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+UIViewController.swift b/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+UIViewController.swift new file mode 100644 index 00000000..59284cce --- /dev/null +++ b/TIDeveloperUtils/Sources/DashedBoundsLayer/DashedBoundsLayer+UIViewController.swift @@ -0,0 +1,37 @@ +// +// Copyright (c) 2024 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 UIViewController { + + @discardableResult + func debugBoundsVisually(debugSubviews: Bool = true, + filterSubviews isIncluded: @escaping (UIView) -> Bool = { _ in true }, + after delay: DispatchTimeInterval? = nil) -> Self { + + view.debugBoundsVisually(debugSubviews: debugSubviews, + after: delay, + filterSubviews: isIncluded) + return self + } +} diff --git a/TIDeveloperUtils/TIDeveloperUtils.podspec b/TIDeveloperUtils/TIDeveloperUtils.podspec index 0e6c8e08..f324837b 100644 --- a/TIDeveloperUtils/TIDeveloperUtils.podspec +++ b/TIDeveloperUtils/TIDeveloperUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIDeveloperUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Universal web view API' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIEcommerce/TIEcommerce.podspec b/TIEcommerce/TIEcommerce.podspec index 7dde39bc..96cdee73 100644 --- a/TIEcommerce/TIEcommerce.podspec +++ b/TIEcommerce/TIEcommerce.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIEcommerce' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Cart, products, promocodes, bonuses and other related actions' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIFoundationUtils/TIFoundationUtils.podspec b/TIFoundationUtils/TIFoundationUtils.podspec index 02268c46..db9895cb 100644 --- a/TIFoundationUtils/TIFoundationUtils.podspec +++ b/TIFoundationUtils/TIFoundationUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIFoundationUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Set of helpers for Foundation framework classes.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIGoogleMapUtils/TIGoogleMapUtils.podspec b/TIGoogleMapUtils/TIGoogleMapUtils.podspec index 6525d900..6be30326 100644 --- a/TIGoogleMapUtils/TIGoogleMapUtils.podspec +++ b/TIGoogleMapUtils/TIGoogleMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIGoogleMapUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Set of helpers for map objects clustering and interacting using Google Maps SDK.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIKeychainUtils/TIKeychainUtils.podspec b/TIKeychainUtils/TIKeychainUtils.podspec index 535e0ebf..2c95a764 100644 --- a/TIKeychainUtils/TIKeychainUtils.podspec +++ b/TIKeychainUtils/TIKeychainUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIKeychainUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Set of helpers for Keychain classes.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TILogging/TILogging.podspec b/TILogging/TILogging.podspec index f27e2ce9..b5a16b63 100644 --- a/TILogging/TILogging.podspec +++ b/TILogging/TILogging.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TILogging' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Logging for TI libraries.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIMapUtils/TIMapUtils.podspec b/TIMapUtils/TIMapUtils.podspec index 125904e5..fc95d9d4 100644 --- a/TIMapUtils/TIMapUtils.podspec +++ b/TIMapUtils/TIMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIMapUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Set of helpers for map objects clustering and interacting.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIMoyaNetworking/TIMoyaNetworking.podspec b/TIMoyaNetworking/TIMoyaNetworking.podspec index b59a6b94..2c983898 100644 --- a/TIMoyaNetworking/TIMoyaNetworking.podspec +++ b/TIMoyaNetworking/TIMoyaNetworking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIMoyaNetworking' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Moya + Swagger network service.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TINetworking/TINetworking.podspec b/TINetworking/TINetworking.podspec index af2c0480..42108109 100644 --- a/TINetworking/TINetworking.podspec +++ b/TINetworking/TINetworking.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TINetworking' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Swagger-frendly networking layer helpers.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TINetworkingCache/TINetworkingCache.podspec b/TINetworkingCache/TINetworkingCache.podspec index 11813404..520054f7 100644 --- a/TINetworkingCache/TINetworkingCache.podspec +++ b/TINetworkingCache/TINetworkingCache.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TINetworkingCache' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Caching results of EndpointRequests.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIPagination/TIPagination.podspec b/TIPagination/TIPagination.podspec index fff8119e..1d555948 100644 --- a/TIPagination/TIPagination.podspec +++ b/TIPagination/TIPagination.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIPagination' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Generic pagination component.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TISwiftUICore/TISwiftUICore.podspec b/TISwiftUICore/TISwiftUICore.podspec index f5a40a29..c39a624e 100644 --- a/TISwiftUICore/TISwiftUICore.podspec +++ b/TISwiftUICore/TISwiftUICore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUICore' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Core UI elements: protocols, views and helpers.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TISwiftUtils/TISwiftUtils.podspec b/TISwiftUtils/TISwiftUtils.podspec index 6a33f57d..6887bde9 100644 --- a/TISwiftUtils/TISwiftUtils.podspec +++ b/TISwiftUtils/TISwiftUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TISwiftUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Bunch of useful helpers for Swift development.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITableKitUtils/TITableKitUtils.podspec b/TITableKitUtils/TITableKitUtils.podspec index 64cba57a..ca9c36bb 100644 --- a/TITableKitUtils/TITableKitUtils.podspec +++ b/TITableKitUtils/TITableKitUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITableKitUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Set of helpers for TableKit classes.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TITextProcessing/TITextProcessing.podspec b/TITextProcessing/TITextProcessing.podspec index 8185698f..037d4926 100644 --- a/TITextProcessing/TITextProcessing.podspec +++ b/TITextProcessing/TITextProcessing.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TITextProcessing' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'A text processing service helping to get a text mask and a placeholder from incoming regex.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIElements/Sources/Views/Skeletons/Configuration/BaseViewSkeletonsConfiguration.swift b/TIUIElements/Sources/Views/Skeletons/Configuration/BaseViewSkeletonsConfiguration.swift index 6a99545e..df9e3428 100644 --- a/TIUIElements/Sources/Views/Skeletons/Configuration/BaseViewSkeletonsConfiguration.swift +++ b/TIUIElements/Sources/Views/Skeletons/Configuration/BaseViewSkeletonsConfiguration.swift @@ -26,9 +26,13 @@ import UIKit open class BaseViewSkeletonsConfiguration { open class Defaults { - public static var cornerRadius: CGFloat { + public class var cornerRadius: CGFloat { 16 } + + public class var animatable: Bool { + true + } } public enum Shape { @@ -40,14 +44,17 @@ open class BaseViewSkeletonsConfiguration { public var padding: UIEdgeInsets public var maxWidth: CGFloat public var shape: Shape + public var animatable: Bool public init(padding: UIEdgeInsets = .edges(5), maxWidth: CGFloat = .infinity, - shape: Shape = .rectangle(cornerRadius: Defaults.cornerRadius)) { + shape: Shape = .rectangle(cornerRadius: Defaults.cornerRadius), + animatable: Bool = Defaults.animatable) { self.shape = shape self.maxWidth = maxWidth self.padding = padding + self.animatable = animatable } open func createPath(for rect: CGRect) -> CGPath { @@ -74,10 +81,12 @@ open class BaseViewSkeletonsConfiguration { open func copyWith(padding: UIEdgeInsets? = nil, maxWidth: CGFloat? = nil, - shape: Shape? = nil) -> BaseViewSkeletonsConfiguration { + shape: Shape? = nil, + animatabel: Bool? = nil) -> BaseViewSkeletonsConfiguration { BaseViewSkeletonsConfiguration(padding: padding ?? self.padding, maxWidth: maxWidth ?? self.maxWidth, - shape: shape ?? self.shape) + shape: shape ?? self.shape, + animatable: animatabel ?? self.animatable) } } diff --git a/TIUIElements/Sources/Views/Skeletons/Configuration/ContainerViewSkeletonsConfiguration.swift b/TIUIElements/Sources/Views/Skeletons/Configuration/ContainerViewSkeletonsConfiguration.swift index 1e898d28..ceac2aa2 100644 --- a/TIUIElements/Sources/Views/Skeletons/Configuration/ContainerViewSkeletonsConfiguration.swift +++ b/TIUIElements/Sources/Views/Skeletons/Configuration/ContainerViewSkeletonsConfiguration.swift @@ -25,31 +25,41 @@ import UIKit open class ContainerViewSkeletonsConfiguration: BaseViewSkeletonsConfiguration { open class Defaults: BaseViewSkeletonsConfiguration.Defaults { - public static var borderWidth: CGFloat { + public class var borderWidth: CGFloat { 1 } } public var borderWidth: CGFloat + public var isContainerHidden: Bool { + borderWidth == .zero + } + public init(borderWidth: CGFloat = Defaults.borderWidth, padding: UIEdgeInsets = .zero, maxWidth: CGFloat = .infinity, - shape: Shape = .rectangle(cornerRadius: Defaults.cornerRadius)) { + shape: Shape = .rectangle(cornerRadius: Defaults.cornerRadius), + animatable: Bool = Defaults.animatable) { self.borderWidth = borderWidth - super.init(padding: padding, shape: shape) + super.init(padding: padding, + maxWidth: maxWidth, + shape: shape, + animatable: animatable) } open func copyWith(borderWidth: CGFloat? = nil, padding: UIEdgeInsets? = nil, maxWidth: CGFloat? = nil, - shape: Shape? = nil) -> ContainerViewSkeletonsConfiguration { + shape: Shape? = nil, + animatable: Bool? = nil) -> ContainerViewSkeletonsConfiguration { ContainerViewSkeletonsConfiguration(borderWidth: borderWidth ?? self.borderWidth, padding: padding ?? self.padding, maxWidth: maxWidth ?? self.maxWidth, - shape: shape ?? self.shape) + shape: shape ?? self.shape, + animatable: animatable ?? self.animatable) } } diff --git a/TIUIElements/Sources/Views/Skeletons/Configuration/HolderViewSkeletonsConfiguration.swift b/TIUIElements/Sources/Views/Skeletons/Configuration/HolderViewSkeletonsConfiguration.swift new file mode 100644 index 00000000..d0eabd82 --- /dev/null +++ b/TIUIElements/Sources/Views/Skeletons/Configuration/HolderViewSkeletonsConfiguration.swift @@ -0,0 +1,77 @@ +// +// Copyright (c) 2024 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 HolderViewSkeletonsConfiguration: ContainerViewSkeletonsConfiguration { + + open class Defaults: ContainerViewSkeletonsConfiguration.Defaults { + public override class var borderWidth: CGFloat { + .zero + } + + public class var backgroundColor: UIColor? { + nil + } + + public override class var cornerRadius: CGFloat { + .zero + } + + public override class var animatable: Bool { + false + } + } + + public var backgroundColor: UIColor? + + public init(backgroundColor: UIColor? = Defaults.backgroundColor, + borderWidth: CGFloat = Defaults.borderWidth, + padding: UIEdgeInsets = .zero, + maxWidth: CGFloat = .infinity, + shape: Shape = .rectangle(cornerRadius: Defaults.cornerRadius), + animatable: Bool = Defaults.animatable) { + + self.backgroundColor = backgroundColor + + super.init(borderWidth: borderWidth, + padding: padding, + maxWidth: maxWidth, + shape: shape, + animatable: animatable) + } + + open func copyWith(backgroundColor: UIColor? = nil, + borderWidth: CGFloat? = nil, + padding: UIEdgeInsets? = nil, + maxWidth: CGFloat? = nil, + shape: Shape? = nil, + animatable: Bool? = nil) -> HolderViewSkeletonsConfiguration { + + HolderViewSkeletonsConfiguration(backgroundColor: backgroundColor ?? self.backgroundColor, + borderWidth: borderWidth ?? self.borderWidth, + padding: padding ?? self.padding, + maxWidth: maxWidth ?? self.maxWidth, + shape: shape ?? self.shape, + animatable: animatable ?? self.animatable) + } +} diff --git a/TIUIElements/Sources/Views/Skeletons/Configuration/SkeletonsConfiguration.swift b/TIUIElements/Sources/Views/Skeletons/Configuration/SkeletonsConfiguration.swift index db603054..760f4c68 100644 --- a/TIUIElements/Sources/Views/Skeletons/Configuration/SkeletonsConfiguration.swift +++ b/TIUIElements/Sources/Views/Skeletons/Configuration/SkeletonsConfiguration.swift @@ -36,37 +36,33 @@ open class SkeletonsConfiguration { public var viewConfiguration: BaseViewSkeletonsConfiguration public var containerViewConfiguration: ContainerViewSkeletonsConfiguration + public var holderViewConfiguration: HolderViewSkeletonsConfiguration public var labelConfiguration: TextSkeletonsConfiguration public var imageViewConfiguration: BaseViewSkeletonsConfiguration public var animation: Closure? - public var baseSkeletonBackgroundColor: CGColor? public var skeletonsBackgroundColor: CGColor public var skeletonsMovingColor: CGColor public weak var configurationDelegate: SkeletonsConfigurationDelegate? - open var isContainersHidden: Bool { - containerViewConfiguration.borderWidth == .zero - } - // MARK: - Init public init(viewConfiguration: BaseViewSkeletonsConfiguration = .init(), containerViewConfiguration: ContainerViewSkeletonsConfiguration = .init(), + holderViewConfiguration: HolderViewSkeletonsConfiguration = .init(), labelConfiguration: TextSkeletonsConfiguration = .init(), imageViewConfiguration: BaseViewSkeletonsConfiguration = .init(shape: .circle), animation: Closure? = Defaults.animation, - baseSkeletonBackgroundColor: UIColor? = nil, skeletonsBackgroundColor: UIColor = .lightGray.withAlphaComponent(0.7), configurationDelegate: SkeletonsConfigurationDelegate? = nil) { self.viewConfiguration = viewConfiguration self.containerViewConfiguration = containerViewConfiguration + self.holderViewConfiguration = holderViewConfiguration self.labelConfiguration = labelConfiguration self.imageViewConfiguration = imageViewConfiguration self.animation = animation - self.baseSkeletonBackgroundColor = baseSkeletonBackgroundColor?.cgColor self.skeletonsBackgroundColor = skeletonsBackgroundColor.cgColor self.skeletonsMovingColor = skeletonsBackgroundColor.withAlphaComponent(0.2).cgColor self.configurationDelegate = configurationDelegate @@ -82,21 +78,18 @@ open class SkeletonsConfiguration { layer.fillColor = skeletonsBackgroundColor } - open func configureBaseViewAppearance(layer: SkeletonLayer, view: UIView) { - layer.fillColor = baseSkeletonBackgroundColor ?? view.backgroundColor?.cgColor + open func configureHolderViewAppearance(layer: SkeletonLayer, view: UIView) { + layer.fillColor = holderViewConfiguration.backgroundColor?.cgColor ?? view.backgroundColor?.cgColor + + configureApparance(of: layer, + with: holderViewConfiguration) } open func configureContainerAppearance(layer: SkeletonLayer) { layer.fillColor = UIColor.clear.cgColor - if !isContainersHidden { - layer.borderColor = skeletonsBackgroundColor - layer.borderWidth = containerViewConfiguration.borderWidth - - if case let .rectangle(cornerRadius: radius) = containerViewConfiguration.shape { - layer.cornerRadius = radius - } - } + configureApparance(of: layer, + with: containerViewConfiguration) } open func configureAppearance(gradientLayer: CAGradientLayer) { @@ -106,4 +99,17 @@ open class SkeletonsConfiguration { skeletonsBackgroundColor, ] } + + private func configureApparance(of layer: SkeletonLayer, + with configuration: some ContainerViewSkeletonsConfiguration) { + + if !configuration.isContainerHidden { + layer.borderColor = skeletonsBackgroundColor + layer.borderWidth = containerViewConfiguration.borderWidth + + if case let .rectangle(cornerRadius: radius) = configuration.shape { + layer.cornerRadius = radius + } + } + } } diff --git a/TIUIElements/Sources/Views/Skeletons/Configuration/TextSkeletonsConfiguration.swift b/TIUIElements/Sources/Views/Skeletons/Configuration/TextSkeletonsConfiguration.swift index 8c2313b3..7f865de6 100644 --- a/TIUIElements/Sources/Views/Skeletons/Configuration/TextSkeletonsConfiguration.swift +++ b/TIUIElements/Sources/Views/Skeletons/Configuration/TextSkeletonsConfiguration.swift @@ -27,19 +27,19 @@ import UIKit open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration { open class Defaults: BaseViewSkeletonsConfiguration.Defaults { - public static var numberOfLines: Int { + public class var numberOfLines: Int { 1 } - public static var multilineNumberOfLines: Int { + public class var multilineNumberOfLines: Int { 3 } - public static var linesWidthFraction: [CGFloat] { + public class var linesWidthFraction: [CGFloat] { [1, 0.65, 0.78] } - public static var defaultFont: UIFont { + public class var defaultFont: UIFont { .systemFont(ofSize: 15) } } @@ -55,14 +55,15 @@ open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration { lineSpacing: CGFloat? = nil, padding: UIEdgeInsets = .edges(5), maxWidth: CGFloat = .infinity, - shape: Shape = .rectangle(cornerRadius: Defaults.cornerRadius)) { + shape: Shape = .rectangle(cornerRadius: Defaults.cornerRadius), + animatable: Bool = Defaults.animatable) { self.numberOfLines = numberOfLines self.linesWidthFraction = linesWidthFraction self.lineHeight = lineHeight self.lineSpacing = lineSpacing - super.init(padding: padding, shape: shape) + super.init(padding: padding, shape: shape, animatable: animatable) } open func createConfiguration(for label: UILabel) -> TextSkeletonsConfiguration { @@ -73,7 +74,8 @@ open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration { lineSpacing: calculateLineSpacing(for: labelFont), padding: padding, maxWidth: maxWidth, - shape: shape) + shape: shape, + animatable: animatable) } open func createConfiguration(for textView: UITextView) -> TextSkeletonsConfiguration { @@ -84,7 +86,8 @@ open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration { lineSpacing: calculateLineSpacing(for: labelFont), padding: padding, maxWidth: maxWidth, - shape: shape) + shape: shape, + animatable: animatable) } open override func createPath(for rect: CGRect) -> CGPath { @@ -192,7 +195,8 @@ open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration { lineSpacing: CGFloat? = nil, padding: UIEdgeInsets? = nil, maxWidth: CGFloat? = nil, - shape: Shape? = nil) -> TextSkeletonsConfiguration { + shape: Shape? = nil, + animatable: Bool? = nil) -> TextSkeletonsConfiguration { TextSkeletonsConfiguration(numberOfLines: numberOfLines ?? self.numberOfLines, linesWidthFraction: linesWidthFraction ?? self.linesWidthFraction, @@ -200,7 +204,8 @@ open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration { lineSpacing: lineSpacing ?? self.lineSpacing, padding: padding ?? self.padding, maxWidth: maxWidth ?? self.maxWidth, - shape: shape ?? self.shape) + shape: shape ?? self.shape, + animatable: animatable ?? self.animatable) } private func getLinesCount(viewNumberOfLines: Int) -> Int { diff --git a/TIUIElements/Sources/Views/Skeletons/SkeletonLayer.swift b/TIUIElements/Sources/Views/Skeletons/SkeletonLayer.swift index 0f936b8a..1aab9ab4 100644 --- a/TIUIElements/Sources/Views/Skeletons/SkeletonLayer.swift +++ b/TIUIElements/Sources/Views/Skeletons/SkeletonLayer.swift @@ -65,11 +65,12 @@ open class SkeletonLayer: CAShapeLayer { private var geometryChangeObservation: Invalidatable? private var applicationStateObservation: NSObjectProtocol? + private var animatable = BaseViewSkeletonsConfiguration.Defaults.animatable + private weak var layerOwnerView: UIView? private weak var skeletonsPresenterView: UIView? public var configuration: SkeletonsConfiguration - public var isSkeletonsHolder: Bool = false public var isAnimating: Bool { animationLayer.animation(forKey: Defaults.animationKeyPath) != nil @@ -151,7 +152,7 @@ open class SkeletonLayer: CAShapeLayer { open func startAnimation() { guard !isAnimating, - !isSkeletonsHolder, + animatable, let animation = configuration.animation?(self) else { return } @@ -173,8 +174,7 @@ open class SkeletonLayer: CAShapeLayer { configuration.configureContainerAppearance(layer: self) case let .skeletonsHolderView(view): - isSkeletonsHolder = true - configuration.configureBaseViewAppearance(layer: self, view: view) + configuration.configureHolderViewAppearance(layer: self, view: view) default: configuration.configureAppearance(layer: self) @@ -191,42 +191,51 @@ open class SkeletonLayer: CAShapeLayer { case let .textView(textView): let textViewConfig = configuration.labelConfiguration.createConfiguration(for: textView) - path = textViewConfig.createPath(for: textView.bounds) - - let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) - frame = textViewConfig.applyPadding(viewFrame: viewFrame) + updateGeometry(rect: rect, + view: textView, + configuration: textViewConfig) case let .label(label): let labelConfig = configuration.labelConfiguration.createConfiguration(for: label) - path = labelConfig.createPath(for: label.bounds) - - let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) - frame = labelConfig.applyPadding(viewFrame: viewFrame) + updateGeometry(rect: rect, + view: label, + configuration: labelConfig) case .imageView(_): - path = configuration.imageViewConfiguration.createPath(for: viewType.view.bounds) - - let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) - frame = configuration.imageViewConfiguration.applyPadding(viewFrame: viewFrame) + updateGeometry(rect: rect, + view: view, + configuration: configuration.imageViewConfiguration) case .parentView(_): - path = configuration.containerViewConfiguration.createPath(for: viewType.view.bounds) - - let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) - frame = configuration.containerViewConfiguration.applyPadding(viewFrame: viewFrame) + updateGeometry(rect: rect, + view: view, + configuration: configuration.containerViewConfiguration) case .leafView(_): - path = configuration.viewConfiguration.createPath(for: viewType.view.bounds) + updateGeometry(rect: rect, + view: view, + configuration: configuration.viewConfiguration) - let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) - frame = configuration.viewConfiguration.applyPadding(viewFrame: viewFrame) - - default: - path = UIBezierPath(roundedRect: rect, cornerRadius: 20).cgPath - frame = rect + case .skeletonsHolderView(_): + updateGeometry(rect: rect, + view: view, + configuration: configuration.holderViewConfiguration) } animationLayer.frame = bounds } + + private func updateGeometry(rect: CGRect, + view: UIView, + configuration: some BaseViewSkeletonsConfiguration) { + + path = configuration.createPath(for: view.bounds) + + let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) + + frame = configuration.applyPadding(viewFrame: viewFrame) + + animatable = configuration.animatable + } } diff --git a/TIUIElements/Sources/Views/Skeletons/UIView+PresentingSkeletons.swift b/TIUIElements/Sources/Views/Skeletons/UIView+PresentingSkeletons.swift index 0b7bb0c7..435d0a87 100644 --- a/TIUIElements/Sources/Views/Skeletons/UIView+PresentingSkeletons.swift +++ b/TIUIElements/Sources/Views/Skeletons/UIView+PresentingSkeletons.swift @@ -39,19 +39,17 @@ public extension UIView { let viewsToSkeletons = viewsToSkeletons ?? skeletonableViews isUserInteractionEnabled = false - configureBaseLayer(withConfiguration: config) + let baseLayer = configureBaseLayer(withConfiguration: config) viewsToSkeletons .flatMap { view in getSkeletonLayer(forView: view, withConfiguration: config) } - .map { layer in - layer.startAnimation() - - return layer - } .insert(onto: self) .skeletonsShown() + .forEach { $0.startAnimation() } + + baseLayer.startAnimation() } func hideSkeletons() { @@ -80,7 +78,7 @@ public extension UIView { let config = (view as? SkeletonsPresenter)?.skeletonsConfiguration ?? conf if view.isSkeletonsContainer { - if !conf.isContainersHidden, !forceNoContainers { + if !conf.containerViewConfiguration.isContainerHidden, !forceNoContainers { let skeletonLayer = config.createSkeletonLayer(for: self) skeletonLayer.bind(to: .parentView(view)) @@ -102,11 +100,14 @@ public extension UIView { } } - private func configureBaseLayer(withConfiguration conf: SkeletonsConfiguration) { + @discardableResult + private func configureBaseLayer(withConfiguration conf: SkeletonsConfiguration) -> SkeletonLayer { let skeletonLayer = conf.createSkeletonLayer(for: self) skeletonLayer.bind(to: .skeletonsHolderView(self)) layer.insertSublayer(skeletonLayer, at: .max) + + return skeletonLayer } } diff --git a/TIUIElements/TIUIElements.app/Contents/MacOS/TIUIElements.playground/Pages/Skeletons.xcplaygroundpage/Contents.swift b/TIUIElements/TIUIElements.app/Contents/MacOS/TIUIElements.playground/Pages/Skeletons.xcplaygroundpage/Contents.swift index 15b10774..cfa6cacb 100644 --- a/TIUIElements/TIUIElements.app/Contents/MacOS/TIUIElements.playground/Pages/Skeletons.xcplaygroundpage/Contents.swift +++ b/TIUIElements/TIUIElements.app/Contents/MacOS/TIUIElements.playground/Pages/Skeletons.xcplaygroundpage/Contents.swift @@ -195,8 +195,8 @@ let confWithTopToBottomAnim = SkeletonsConfiguration(animation: { _ in */ let confWithRedBackgroundColor = SkeletonsConfiguration(skeletonsBackgroundColor: .red) -//: Для скрытия view от пользователя скелетонами накладывается специальный CALayer, чей цвет заполнения равен backgroundColor view, с которой были запущены скелетоны. Однако этот цвет можно переопределить переопределить с помощью параметра `baseSkeletonBackgroundColor` -let confWithRedBaseBackgroundColor = SkeletonsConfiguration(baseSkeletonBackgroundColor: .red) +//: Для скрытия view от пользователя скелетонами накладывается специальный CALayer, чей цвет заполнения равен backgroundColor view, с которой были запущены скелетоны. Однако этот цвет можно переопределить переопределить с помощью параметра `HolderViewSkeletonsConfiguration.backgroundColor` +let confWithRedBaseBackgroundColor = SkeletonsConfiguration(holderViewConfiguration: .init(backgroundColor: .red)) /*: ### Форма diff --git a/TIUIElements/TIUIElements.podspec b/TIUIElements/TIUIElements.podspec index 0bdc4616..59fc9e8c 100644 --- a/TIUIElements/TIUIElements.podspec +++ b/TIUIElements/TIUIElements.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIElements' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Bunch of useful protocols and views.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIUIKitCore/TIUIKitCore.podspec b/TIUIKitCore/TIUIKitCore.podspec index f4b065a7..02638b41 100644 --- a/TIUIKitCore/TIUIKitCore.podspec +++ b/TIUIKitCore/TIUIKitCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIUIKitCore' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Core UI elements: protocols, views and helpers.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIWebView/TIWebView.podspec b/TIWebView/TIWebView.podspec index 580f9e5b..dd32ff03 100644 --- a/TIWebView/TIWebView.podspec +++ b/TIWebView/TIWebView.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIWebView' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Universal web view API' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/TIYandexMapUtils/TIYandexMapUtils.podspec b/TIYandexMapUtils/TIYandexMapUtils.podspec index 8ff316ef..af8780f9 100644 --- a/TIYandexMapUtils/TIYandexMapUtils.podspec +++ b/TIYandexMapUtils/TIYandexMapUtils.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'TIYandexMapUtils' - s.version = '1.55.1' + s.version = '1.56.0' s.summary = 'Set of helpers for map objects clustering and interacting using Yandex Maps SDK.' s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name s.license = { :type => 'MIT', :file => 'LICENSE' } diff --git a/docs/tiuielements/skeletons.md b/docs/tiuielements/skeletons.md index dfb2ccde..01c58c33 100644 --- a/docs/tiuielements/skeletons.md +++ b/docs/tiuielements/skeletons.md @@ -204,10 +204,10 @@ let confWithTopToBottomAnim = SkeletonsConfiguration(animation: { _ in let confWithRedBackgroundColor = SkeletonsConfiguration(skeletonsBackgroundColor: .red) ``` -Для скрытия view от пользователя скелетонами накладывается специальный CALayer, чей цвет заполнения равен backgroundColor view, с которой были запущены скелетоны. Однако этот цвет можно переопределить переопределить с помощью параметра `baseSkeletonBackgroundColor` +Для скрытия view от пользователя скелетонами накладывается специальный CALayer, чей цвет заполнения равен backgroundColor view, с которой были запущены скелетоны. Однако этот цвет можно переопределить переопределить с помощью параметра `HolderViewSkeletonsConfiguration.backgroundColor` ```swift -let confWithRedBaseBackgroundColor = SkeletonsConfiguration(baseSkeletonBackgroundColor: .red) +let confWithRedBaseBackgroundColor = SkeletonsConfiguration(holderViewConfiguration: .init(backgroundColor: .red)) ``` ### Форма -- 2.40.1