diff --git a/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+AssociatedObjects.swift b/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+AssociatedObjects.swift index 25dd87fb..abe4840d 100644 --- a/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+AssociatedObjects.swift +++ b/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+AssociatedObjects.swift @@ -31,7 +31,8 @@ enum ViewAssociatedKeys { static var skeletonCornerRadius = "skeletonCornerRadius" static var disabledWhenSkeletonIsActive = "disabledWhenSkeletonIsActive" static var delayedShowSkeletonWorkItem = "delayedShowSkeletonWorkItem" - + static var isPerformingSwizzleLayoutSubviews = "isPerformingSwizzleLayoutSubviews" + static var isPerformingSwizzleTraitCollectionDidChange = "isPerformingSwizzleTraitCollectionDidChange" } // codebeat:enable[TOO_MANY_IVARS] @@ -91,4 +92,14 @@ extension UIView { get { return ao_get(pkey: &ViewAssociatedKeys.skeletonCornerRadius) as? Float ?? SkeletonViewAppearance.shared.skeletonCornerRadius } set { ao_set(newValue, pkey: &ViewAssociatedKeys.skeletonCornerRadius) } } + + var _isPerformingSwizzleLayoutSubviews: Bool { + get { return ao_get(pkey: &ViewAssociatedKeys.isPerformingSwizzleLayoutSubviews) as? Bool ?? false } + set { ao_set(newValue, pkey: &ViewAssociatedKeys.isPerformingSwizzleLayoutSubviews) } + } + + var _isPerformingSwizzleTraitCollectionDidChange: Bool { + get { return ao_get(pkey: &ViewAssociatedKeys.isPerformingSwizzleTraitCollectionDidChange) as? Bool ?? false } + set { ao_set(newValue, pkey: &ViewAssociatedKeys.isPerformingSwizzleTraitCollectionDidChange) } + } } diff --git a/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+Swizzling.swift b/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+Swizzling.swift index 3c257161..c011e74f 100644 --- a/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+Swizzling.swift +++ b/SkeletonViewCore/Sources/Internal/UIKitExtensions/UIView+Swizzling.swift @@ -16,61 +16,65 @@ import UIKit extension UIView { @objc func skeletonLayoutSubviews() { - guard Thread.isMainThread else { return } - skeletonLayoutSubviews() + // Use flag to prevent recursive calls + if _isPerformingSwizzleLayoutSubviews || !Thread.isMainThread { return } + + _isPerformingSwizzleLayoutSubviews = true + defer { _isPerformingSwizzleLayoutSubviews = false } + + self.skeletonLayoutSubviews() + guard sk.isSkeletonActive else { return } layoutSkeletonIfNeeded() } @objc func skeletonTraitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - skeletonTraitCollectionDidChange(previousTraitCollection) + // Use flag to prevent recursive calls + if _isPerformingSwizzleTraitCollectionDidChange { return } + + _isPerformingSwizzleTraitCollectionDidChange = true + defer { _isPerformingSwizzleTraitCollectionDidChange = false } + + self.skeletonTraitCollectionDidChange(previousTraitCollection) + guard isSkeletonable, sk.isSkeletonActive, let config = _currentSkeletonConfig else { return } updateSkeleton(skeletonConfig: config) } - + func swizzleLayoutSubviews() { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { - DispatchQueue.once(token: "UIView.SkeletonView.swizzleLayoutSubviews") { - swizzle(selector: #selector(UIView.layoutSubviews), - with: #selector(UIView.skeletonLayoutSubviews), - inClass: UIView.self, - usingClass: UIView.self) - self.layoutSkeletonIfNeeded() - } + DispatchQueue.once(token: "UIView.SkeletonView.swizzleLayoutSubviews") { + swizzle(selector: #selector(UIView.layoutSubviews), + with: #selector(UIView.skeletonLayoutSubviews), + inClass: UIView.self, + usingClass: UIView.self) + self.layoutSkeletonIfNeeded() } } - + func unSwizzleLayoutSubviews() { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { - DispatchQueue.removeOnce(token: "UIView.SkeletonView.swizzleLayoutSubviews") { - swizzle(selector: #selector(UIView.skeletonLayoutSubviews), - with: #selector(UIView.layoutSubviews), - inClass: UIView.self, - usingClass: UIView.self) - } + DispatchQueue.removeOnce(token: "UIView.SkeletonView.swizzleLayoutSubviews") { + swizzle(selector: #selector(UIView.skeletonLayoutSubviews), + with: #selector(UIView.layoutSubviews), + inClass: UIView.self, + usingClass: UIView.self) } } - + func swizzleTraitCollectionDidChange() { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { - DispatchQueue.once(token: "UIView.SkeletonView.swizzleTraitCollectionDidChange") { - swizzle(selector: #selector(UIView.traitCollectionDidChange(_:)), - with: #selector(UIView.skeletonTraitCollectionDidChange(_:)), - inClass: UIView.self, - usingClass: UIView.self) - } + DispatchQueue.once(token: "UIView.SkeletonView.swizzleTraitCollectionDidChange") { + swizzle(selector: #selector(UIView.traitCollectionDidChange(_:)), + with: #selector(UIView.skeletonTraitCollectionDidChange(_:)), + inClass: UIView.self, + usingClass: UIView.self) } } - + func unSwizzleTraitCollectionDidChange() { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { - DispatchQueue.removeOnce(token: "UIView.SkeletonView.swizzleTraitCollectionDidChange") { - swizzle(selector: #selector(UIView.skeletonTraitCollectionDidChange(_:)), - with: #selector(UIView.traitCollectionDidChange(_:)), - inClass: UIView.self, - usingClass: UIView.self) - } + DispatchQueue.removeOnce(token: "UIView.SkeletonView.swizzleTraitCollectionDidChange") { + swizzle(selector: #selector(UIView.skeletonTraitCollectionDidChange(_:)), + with: #selector(UIView.traitCollectionDidChange(_:)), + inClass: UIView.self, + usingClass: UIView.self) } } - }