diff --git a/TIUIElements/Sources/Views/Skeletons/Configuration/SkeletonsConfiguration.swift b/TIUIElements/Sources/Views/Skeletons/Configuration/SkeletonsConfiguration.swift index 0e8f449b..c46c8e62 100644 --- a/TIUIElements/Sources/Views/Skeletons/Configuration/SkeletonsConfiguration.swift +++ b/TIUIElements/Sources/Views/Skeletons/Configuration/SkeletonsConfiguration.swift @@ -31,6 +31,7 @@ open class SkeletonsConfiguration { public var imageViewConfiguration: BaseViewSkeletonsConfiguration public var animation: Closure? + public var baseSkeletonBackgroundColor: CGColor? public var skeletonsBackgroundColor: CGColor public var skeletonsMovingColor: CGColor @@ -47,6 +48,7 @@ open class SkeletonsConfiguration { labelConfiguration: TextSkeletonsConfiguration = .init(), imageViewConfiguration: BaseViewSkeletonsConfiguration = .init(shape: .circle), animation: Closure? = nil, + baseSkeletonBackgroundColor: UIColor? = nil, skeletonsBackgroundColor: UIColor = .lightGray.withAlphaComponent(0.7), configurationDelegate: SkeletonsConfigurationDelegate? = nil) { @@ -55,6 +57,7 @@ open class SkeletonsConfiguration { 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 @@ -70,6 +73,10 @@ open class SkeletonsConfiguration { layer.fillColor = skeletonsBackgroundColor } + open func configureBaseViewAppearance(layer: SkeletonLayer, view: UIView) { + layer.fillColor = baseSkeletonBackgroundColor ?? view.backgroundColor?.cgColor + } + open func configureContainerAppearance(layer: SkeletonLayer) { layer.fillColor = UIColor.clear.cgColor diff --git a/TIUIElements/Sources/Views/Skeletons/Helpers/CGRect+Padding.swift b/TIUIElements/Sources/Views/Skeletons/Helpers/CGRect+Padding.swift index 5472f5d5..f6a585c5 100644 --- a/TIUIElements/Sources/Views/Skeletons/Helpers/CGRect+Padding.swift +++ b/TIUIElements/Sources/Views/Skeletons/Helpers/CGRect+Padding.swift @@ -26,8 +26,8 @@ extension CGRect { func with(padding: UIEdgeInsets) -> CGRect { CGRect(x: minX + padding.left, y: minY + padding.top, - width: width - padding.right, - height: height - padding.bottom) + width: width, + height: height) } func reduceSize(byPadding p: UIEdgeInsets) -> CGRect { diff --git a/TIUIElements/Sources/Views/Skeletons/SkeletonLayer.swift b/TIUIElements/Sources/Views/Skeletons/SkeletonLayer.swift index 6e339243..d645dc21 100644 --- a/TIUIElements/Sources/Views/Skeletons/SkeletonLayer.swift +++ b/TIUIElements/Sources/Views/Skeletons/SkeletonLayer.swift @@ -31,6 +31,7 @@ open class SkeletonLayer: CAShapeLayer { } public enum ViewType { + case base(UIView) case generic(UIView) case container(UIView) case label(UILabel) @@ -53,6 +54,9 @@ open class SkeletonLayer: CAShapeLayer { case let .generic(view): return view + + case let .base(view): + return view } } } @@ -140,6 +144,9 @@ open class SkeletonLayer: CAShapeLayer { case .container(_): configuration.configureContainerAppearance(layer: self) + case .base(_): + configuration.configureBaseViewAppearance(layer: self, view: type.view) + default: configuration.configureAppearance(layer: self) } @@ -153,23 +160,37 @@ open class SkeletonLayer: CAShapeLayer { switch viewType { case let .textView(textView): path = configuration.labelConfiguration.configureTextViewPath(textView: textView) - frame = configuration.labelConfiguration.applyPadding(viewFrame: rect) + + let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) + frame = configuration.labelConfiguration.applyPadding(viewFrame: viewFrame) case let .label(label): path = configuration.labelConfiguration.configureLabelPath(label: label) - frame = configuration.labelConfiguration.applyPadding(viewFrame: rect) + + let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) + frame = configuration.labelConfiguration.applyPadding(viewFrame: viewFrame) case .imageView(_): path = configuration.imageViewConfiguration.drawPath(rect: viewType.view.bounds) - frame = configuration.imageViewConfiguration.applyPadding(viewFrame: rect) + + let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) + frame = configuration.imageViewConfiguration.applyPadding(viewFrame: viewFrame) case .container(_): path = configuration.containerViewConfiguration.drawPath(rect: viewType.view.bounds) - frame = configuration.containerViewConfiguration.applyPadding(viewFrame: rect) + + let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size) + frame = configuration.containerViewConfiguration.applyPadding(viewFrame: viewFrame) case .generic(_): path = configuration.viewConfiguration.drawPath(rect: viewType.view.bounds) - frame = configuration.viewConfiguration.applyPadding(viewFrame: rect) + + 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 } animationLayer.frame = bounds diff --git a/TIUIElements/Sources/Views/Skeletons/UIView+PresentingSkeletons.swift b/TIUIElements/Sources/Views/Skeletons/UIView+PresentingSkeletons.swift index 9900ece8..4eaedd38 100644 --- a/TIUIElements/Sources/Views/Skeletons/UIView+PresentingSkeletons.swift +++ b/TIUIElements/Sources/Views/Skeletons/UIView+PresentingSkeletons.swift @@ -20,6 +20,7 @@ // THE SOFTWARE. // +import TISwiftUtils import UIKit extension UIView { @@ -37,7 +38,7 @@ extension UIView { let viewsToSkeletone = viewsToSkeletone ?? skeletonableViews isUserInteractionEnabled = false - subviews.forEach { $0.isHidden = true } + configureBaseLayer(withConfiguration: config) viewsToSkeletone .flatMap { view in @@ -59,8 +60,6 @@ extension UIView { layer.remove(from: self) } } - - subviews.forEach { $0.isHidden = false } } public func startAnimation() { @@ -78,18 +77,19 @@ extension UIView { // MARK: - Private methods private func getSkeletonLayer(forView view: UIView, - withConfiguration conf: SkeletonsConfiguration) -> [SkeletonLayer] { + withConfiguration conf: SkeletonsConfiguration, + forceNoContainers: Bool = false) -> [SkeletonLayer] { let skeletonLayer = conf.createSkeletonLayer(for: self) var subviewSkeletonLayers = [SkeletonLayer]() if view.isSkeletonsContainer { - if !conf.isContainersHidden { + if !conf.isContainersHidden, !forceNoContainers { skeletonLayer.bind(to: .container(view)) } subviewSkeletonLayers = view.skeletonableViews - .map { getSkeletonLayer(forView: $0, withConfiguration: conf) } + .map { getSkeletonLayer(forView: $0, withConfiguration: conf, forceNoContainers: true) } .flatMap { $0 } } else { @@ -98,6 +98,13 @@ extension UIView { return [skeletonLayer] + subviewSkeletonLayers } + + private func configureBaseLayer(withConfiguration conf: SkeletonsConfiguration) { + let skeletonLayer = conf.createSkeletonLayer(for: self) + + skeletonLayer.bind(to: .base(self)) + layer.insertSublayer(skeletonLayer, at: .max) + } } // MARK: - Helper extension 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 f7d7b032..fffb4049 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 @@ -189,6 +189,9 @@ let confWithTopToBottomAnim = SkeletonsConfiguration(animation: { _ in */ let confWithRedBackgroundColor = SkeletonsConfiguration(skeletonsBackgroundColor: .red) +//: Для скрытия view от пользователя скелетонами накладывается специальный CALayer, чей цвет заполнения равен backgroundColor view, с которой были запущены скелетоны. Однако этот цвет можно переопределить переопределить с помощью параметра `baseSkeletonBackgroundColor` +let confWithRedBaseBackgroundColor = SkeletonsConfiguration(baseSkeletonBackgroundColor: .red) + /*: ### Форма diff --git a/docs/tiuielements/skeletons.md b/docs/tiuielements/skeletons.md index 71f55665..3d6fd882 100644 --- a/docs/tiuielements/skeletons.md +++ b/docs/tiuielements/skeletons.md @@ -198,6 +198,12 @@ let confWithTopToBottomAnim = SkeletonsConfiguration(animation: { _ in let confWithRedBackgroundColor = SkeletonsConfiguration(skeletonsBackgroundColor: .red) ``` +Для скрытия view от пользователя скелетонами накладывается специальный CALayer, чей цвет заполнения равен backgroundColor view, с которой были запущены скелетоны. Однако этот цвет можно переопределить переопределить с помощью параметра `baseSkeletonBackgroundColor` + +```swift +let confWithRedBaseBackgroundColor = SkeletonsConfiguration(baseSkeletonBackgroundColor: .red) +``` + ### Форма Форму можно настраивать отдельно для `UILabel`, `UITextView`, `UIImageView` и остальных вью. Например, картинки можно сделать круглыми, а лейблы прямоугольные с закругленными краями: