diff --git a/SkeletonViewCore/Sources/Internal/Models/RecoverableViewState.swift b/SkeletonViewCore/Sources/Internal/Models/RecoverableViewState.swift index 69d2e0ec..d7348285 100644 --- a/SkeletonViewCore/Sources/Internal/Models/RecoverableViewState.swift +++ b/SkeletonViewCore/Sources/Internal/Models/RecoverableViewState.swift @@ -13,12 +13,14 @@ struct RecoverableViewState { var backgroundColor: UIColor? var cornerRadius: CGFloat var clipToBounds: Bool + var borderColor: CGColor? var isUserInteractionsEnabled: Bool init(view: UIView) { self.backgroundColor = view.backgroundColor self.clipToBounds = view.layer.masksToBounds self.cornerRadius = view.layer.cornerRadius + self.borderColor = view.layer.borderColor self.isUserInteractionsEnabled = view.isUserInteractionEnabled } @@ -70,10 +72,20 @@ struct RecoverableImageViewState { } struct RecoverableButtonViewState { + var state: UIControl.State + var attributedTitle: NSAttributedString? var title: String? + var titleColor: UIColor? + var image: UIImage? + var backgroundImage: UIImage? init(view: UIButton) { - self.title = view.titleLabel?.text + self.state = view.state + self.attributedTitle = view.attributedTitle(for: state) + self.title = view.title(for: state) + self.titleColor = view.titleColor(for: state) + self.image = view.image(for: state) + self.backgroundImage = view.backgroundImage(for: state) } } diff --git a/SkeletonViewCore/Sources/Internal/Models/SkeletonLayer.swift b/SkeletonViewCore/Sources/Internal/Models/SkeletonLayer.swift index e5c5b146..03b14b84 100755 --- a/SkeletonViewCore/Sources/Internal/Models/SkeletonLayer.swift +++ b/SkeletonViewCore/Sources/Internal/Models/SkeletonLayer.swift @@ -27,6 +27,7 @@ struct SkeletonLayer { self.maskLayer.anchorPoint = .zero self.maskLayer.bounds = holder.definedMaxBounds self.maskLayer.cornerRadius = CGFloat(holder.skeletonCornerRadius) + self.maskLayer.zPosition = CGFloat(Float.greatestFiniteMagnitude) // CoreAnimation complains if CGFloat is used addTextLinesIfNeeded() self.maskLayer.tint(withColors: colors, traitCollection: holder.traitCollection) } diff --git a/SkeletonViewCore/Sources/Internal/SkeletonExtensions/PrepareViewForSkeleton.swift b/SkeletonViewCore/Sources/Internal/SkeletonExtensions/PrepareViewForSkeleton.swift index 660e6683..49a6388b 100644 --- a/SkeletonViewCore/Sources/Internal/SkeletonExtensions/PrepareViewForSkeleton.swift +++ b/SkeletonViewCore/Sources/Internal/SkeletonExtensions/PrepareViewForSkeleton.swift @@ -22,6 +22,7 @@ extension UIView { startTransition { [weak self] in self?.backgroundColor = .clear + self?.layer.borderColor = nil } } @@ -101,7 +102,13 @@ extension UIButton { } startTransition { [weak self] in - self?.setTitle(nil, for: .normal) + guard let self = self else { return } + + self.setTitle(nil, for: self.state) + self.setTitleColor(nil, for: self.state) + self.setAttributedTitle(nil, for: self.state) + self.setImage(nil, for: self.state) + self.setBackgroundImage(nil, for: self.state) } } diff --git a/SkeletonViewCore/Sources/Internal/SkeletonExtensions/Recoverable.swift b/SkeletonViewCore/Sources/Internal/SkeletonExtensions/Recoverable.swift index a4e644c5..79f1051c 100644 --- a/SkeletonViewCore/Sources/Internal/SkeletonExtensions/Recoverable.swift +++ b/SkeletonViewCore/Sources/Internal/SkeletonExtensions/Recoverable.swift @@ -32,6 +32,7 @@ extension UIView: Recoverable { self.layer.cornerRadius = storedViewState.cornerRadius self.layer.masksToBounds = storedViewState.clipToBounds + self.layer.borderColor = storedViewState.borderColor if self.isUserInteractionDisabledWhenSkeletonIsActive { self.isUserInteractionEnabled = storedViewState.isUserInteractionsEnabled @@ -177,8 +178,26 @@ extension UIButton { override func recoverViewState(forced: Bool) { super.recoverViewState(forced: forced) startTransition { [weak self] in - if self?.title(for: .normal) == nil { - self?.setTitle(self?.buttonState?.title, for: .normal) + guard let self = self, let buttonState = self.buttonState else { return } + + let state = buttonState.state + + if let attributedTitle = buttonState.attributedTitle, self.attributedTitle(for: state) == nil || forced { + self.setAttributedTitle(attributedTitle, for: state) + } else if let title = buttonState.title, self.title(for: state) == nil || forced { + self.setTitle(title, for: state) + + if let titleColor = buttonState.titleColor, self.titleColor(for: state) == nil || forced { + self.setTitleColor(titleColor, for: state) + } + } + + if let image = buttonState.image, self.image(for: state) == nil || forced { + self.setImage(image, for: state) + } + + if let backgroundImage = buttonState.backgroundImage, self.backgroundImage(for: state) == nil || forced { + self.setBackgroundImage(backgroundImage, for: state) } } } diff --git a/SkeletonViewCore/Sources/Internal/UIKitExtensions/CALayer+Extensions.swift b/SkeletonViewCore/Sources/Internal/UIKitExtensions/CALayer+Extensions.swift index fc4bc637..6531f6c7 100644 --- a/SkeletonViewCore/Sources/Internal/UIKitExtensions/CALayer+Extensions.swift +++ b/SkeletonViewCore/Sources/Internal/UIKitExtensions/CALayer+Extensions.swift @@ -51,12 +51,12 @@ extension CALayer { } } - func playAnimation(_ anim: SkeletonLayerAnimation, key: String, completion: (() -> Void)? = nil) { + func playAnimation(_ anim: @escaping SkeletonLayerAnimation, key: String, completion: (() -> Void)? = nil) { skeletonSublayers.recursiveSearch(leafBlock: { - DispatchQueue.main.async { CATransaction.begin() } - DispatchQueue.main.async { CATransaction.setCompletionBlock(completion) } - add(anim(self), forKey: key) - DispatchQueue.main.async { CATransaction.commit() } + CATransaction.begin() + CATransaction.setCompletionBlock(completion) + self.add(anim(self), forKey: key) + CATransaction.commit() }) { $0.playAnimation(anim, key: key, completion: completion) } @@ -71,22 +71,22 @@ extension CALayer { } func setOpacity(from: Int, to: Int, duration: TimeInterval, completion: (() -> Void)?) { - DispatchQueue.main.async { CATransaction.begin() } + CATransaction.begin() + CATransaction.setCompletionBlock(completion) let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity)) animation.fromValue = from animation.toValue = to animation.duration = duration animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) - DispatchQueue.main.async { CATransaction.setCompletionBlock(completion) } - add(animation, forKey: "setOpacityAnimation") - DispatchQueue.main.async { CATransaction.commit() } + self.add(animation, forKey: "setOpacityAnimation") + CATransaction.commit() } func insertSkeletonLayer(_ sublayer: SkeletonLayer, atIndex index: UInt32, transition: SkeletonTransitionStyle, completion: (() -> Void)? = nil) { insertSublayer(sublayer.contentLayer, at: index) switch transition { case .none: - DispatchQueue.main.async { completion?() } + completion?() case .crossDissolve(let duration): sublayer.contentLayer.setOpacity(from: 0, to: 1, duration: duration, completion: completion) } diff --git a/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+Transitions.swift b/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+Transitions.swift index 66b3a610..aa31c661 100644 --- a/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+Transitions.swift +++ b/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+Transitions.swift @@ -7,7 +7,7 @@ extension UIView { func startTransition(transitionBlock: @escaping () -> Void) { guard let transitionStyle = _currentSkeletonConfig?.transition, transitionStyle != .none else { - transitionBlock() + UIView.performWithoutAnimation(transitionBlock) return }