diff --git a/FluentDarkModeKit.xcodeproj/project.pbxproj b/FluentDarkModeKit.xcodeproj/project.pbxproj index 2b3f73f..00bbf76 100644 --- a/FluentDarkModeKit.xcodeproj/project.pbxproj +++ b/FluentDarkModeKit.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 8C071AEC23683BF5001AB7B2 /* NSObject+DarkModeKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C071AEA23683BF5001AB7B2 /* NSObject+DarkModeKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 8C071AED23683BF5001AB7B2 /* NSObject+DarkModeKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C071AEB23683BF5001AB7B2 /* NSObject+DarkModeKit.m */; }; 8C1915D82361EFDB004A606A /* FluentDarkModeKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C1915CE2361EFDB004A606A /* FluentDarkModeKit.framework */; }; 8C1915DD2361EFDB004A606A /* DarkModeKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C1915DC2361EFDB004A606A /* DarkModeKitTests.swift */; }; 8C63F64323A36B2700D93CF4 /* UIButtonVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C63F64123A36B0900D93CF4 /* UIButtonVC.swift */; }; @@ -56,6 +54,7 @@ 8CDA62A52366DAA9004895B5 /* DMDynamicColor.h in Headers */ = {isa = PBXBuildFile; fileRef = 8CDA628B2366DAA9004895B5 /* DMDynamicColor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8CE066CF239E5582002CE16D /* UIColor+DarkModeKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CC8D86C2398E4EC0043276A /* UIColor+DarkModeKit.m */; }; 8CE066D0239E5586002CE16D /* UIImage+DarkModeKit.m in Sources */ = {isa = PBXBuildFile; fileRef = 8CC8D8722398E7A30043276A /* UIImage+DarkModeKit.m */; }; + EA7316F1248F5055009AE037 /* UILabelVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7316EF248F5050009AE037 /* UILabelVC.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -97,8 +96,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 8C071AEA23683BF5001AB7B2 /* NSObject+DarkModeKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+DarkModeKit.h"; sourceTree = ""; }; - 8C071AEB23683BF5001AB7B2 /* NSObject+DarkModeKit.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+DarkModeKit.m"; sourceTree = ""; }; 8C1915CE2361EFDB004A606A /* FluentDarkModeKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FluentDarkModeKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8C1915D22361EFDB004A606A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 8C1915D72361EFDB004A606A /* FluentDarkModeKitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = FluentDarkModeKitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -151,6 +148,7 @@ 8CDA62892366DAA9004895B5 /* DMDynamicImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DMDynamicImage.m; sourceTree = ""; }; 8CDA628A2366DAA9004895B5 /* DarkModeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarkModeManager.swift; sourceTree = ""; }; 8CDA628B2366DAA9004895B5 /* DMDynamicColor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DMDynamicColor.h; sourceTree = ""; }; + EA7316EF248F5050009AE037 /* UILabelVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UILabelVC.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -216,8 +214,6 @@ 8CDA62892366DAA9004895B5 /* DMDynamicImage.m */, 8CDA62712366DAA9004895B5 /* DMTraitCollection.h */, 8CDA62722366DAA9004895B5 /* DMTraitCollection.m */, - 8C071AEA23683BF5001AB7B2 /* NSObject+DarkModeKit.h */, - 8C071AEB23683BF5001AB7B2 /* NSObject+DarkModeKit.m */, 8CC8D86F2398E7520043276A /* DMNamespace.h */, 8CC8D86B2398E4EC0043276A /* UIColor+DarkModeKit.h */, 8CC8D86C2398E4EC0043276A /* UIColor+DarkModeKit.m */, @@ -291,6 +287,7 @@ 8C7B250A23A22A70002E2558 /* UIActivityIndicatorViewVC.swift */, 8C63F64123A36B0900D93CF4 /* UIButtonVC.swift */, 8C63F64423A374CF00D93CF4 /* UIPageControlVC.swift */, + EA7316EF248F5050009AE037 /* UILabelVC.swift */, 8CAFD9DD23715FAB001A63B8 /* Assets.xcassets */, 8CAFD9DF23715FAB001A63B8 /* LaunchScreen.storyboard */, 8CAFD9E223715FAB001A63B8 /* Info.plist */, @@ -338,7 +335,6 @@ files = ( 8CC8D86D2398E4EC0043276A /* UIColor+DarkModeKit.h in Headers */, 8CC8D8702398E7520043276A /* DMNamespace.h in Headers */, - 8C071AEC23683BF5001AB7B2 /* NSObject+DarkModeKit.h in Headers */, 8CDA628D2366DAA9004895B5 /* DMTraitCollection.h in Headers */, 8CDA628C2366DAA9004895B5 /* FluentDarkModeKit.h in Headers */, 8CB63E41238551F3008ABCE2 /* UIView+DarkModeKit.h in Headers */, @@ -551,7 +547,6 @@ 8CDA629C2366DAA9004895B5 /* UINavigationBar+DarkModeKit.swift in Sources */, 8CDA629D2366DAA9004895B5 /* UITabBar+DarkModeKit.swift in Sources */, 8CDA62922366DAA9004895B5 /* UIToolbar+DarkModeKit.swift in Sources */, - 8C071AED23683BF5001AB7B2 /* NSObject+DarkModeKit.m in Sources */, 8CDA629A2366DAA9004895B5 /* UIPageControl+DarkModeKit.swift in Sources */, 8CDA62932366DAA9004895B5 /* UIButton+DarkModeKit.swift in Sources */, 8CDA629B2366DAA9004895B5 /* UITableView+DarkModeKit.swift in Sources */, @@ -582,6 +577,7 @@ 8C7B250C23A22A80002E2558 /* UIActivityIndicatorViewVC.swift in Sources */, 8CAFD9D923715FAA001A63B8 /* ViewController.swift in Sources */, 8C63F64323A36B2700D93CF4 /* UIButtonVC.swift in Sources */, + EA7316F1248F5055009AE037 /* UILabelVC.swift in Sources */, 8CAFD9ED2371606D001A63B8 /* NavigationController.swift in Sources */, 8CAFD9D523715FAA001A63B8 /* AppDelegate.swift in Sources */, 8C63F64623A374D300D93CF4 /* UIPageControlVC.swift in Sources */, diff --git a/Sources/DarkModeCore/DMDynamicImage.m b/Sources/DarkModeCore/DMDynamicImage.m index f541e99..e14c1a3 100644 --- a/Sources/DarkModeCore/DMDynamicImage.m +++ b/Sources/DarkModeCore/DMDynamicImage.m @@ -5,7 +5,6 @@ #import "DMDynamicImage.h" #import "DMTraitCollection.h" -#import "NSObject+DarkModeKit.h" @import ObjectiveC; diff --git a/Sources/DarkModeCore/FluentDarkModeKit.h b/Sources/DarkModeCore/FluentDarkModeKit.h index 1401c73..a9ee177 100644 --- a/Sources/DarkModeCore/FluentDarkModeKit.h +++ b/Sources/DarkModeCore/FluentDarkModeKit.h @@ -6,7 +6,6 @@ #import #import #import -#import #import #import #import diff --git a/Sources/DarkModeCore/NSObject+DarkModeKit.h b/Sources/DarkModeCore/NSObject+DarkModeKit.h deleted file mode 100644 index 5d38645..0000000 --- a/Sources/DarkModeCore/NSObject+DarkModeKit.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// - -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface NSObject (DarkModeKit) - -/// Swizzle two instance methods for class that calls this method. -/// -/// Return NO if any selector cannot find corresponding method. -+ (BOOL)dm_swizzleInstanceMethod:(SEL)fromSelector to:(SEL)toSelector; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Sources/DarkModeCore/NSObject+DarkModeKit.m b/Sources/DarkModeCore/NSObject+DarkModeKit.m deleted file mode 100644 index 7500731..0000000 --- a/Sources/DarkModeCore/NSObject+DarkModeKit.m +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. -// - -#import "NSObject+DarkModeKit.h" - -@import ObjectiveC; - -@implementation NSObject (DarkModeKit) - -+ (BOOL)dm_swizzleInstanceMethod:(SEL)selector1 to:(SEL)selector2 { - Method method1 = class_getInstanceMethod(self, selector1); - Method method2 = class_getInstanceMethod(self, selector2); - - if (!method1 || !method2) { - return NO; - } - - if (class_addMethod(self, selector1, method_getImplementation(method2), method_getTypeEncoding(method2))) { - class_replaceMethod(self, selector2, method_getImplementation(method1), method_getTypeEncoding(method1)); - } else { - method_exchangeImplementations(method1, method2); - } - - return YES; -} - -@end diff --git a/Sources/DarkModeCore/UIImage+DarkModeKit.h b/Sources/DarkModeCore/UIImage+DarkModeKit.h index f445778..e3d4237 100644 --- a/Sources/DarkModeCore/UIImage+DarkModeKit.h +++ b/Sources/DarkModeCore/UIImage+DarkModeKit.h @@ -14,6 +14,8 @@ NS_ASSUME_NONNULL_BEGIN @interface UIImage (DarkModeKit) ++ (void)dm_swizzleIsEqual; + + (UIImage *)dm_imageWithLightImage:(UIImage *)lightImage darkImage:(UIImage *)darkImage NS_SWIFT_UNAVAILABLE("Use init(_:light:dark:) instead."); diff --git a/Sources/DarkModeCore/UIImage+DarkModeKit.m b/Sources/DarkModeCore/UIImage+DarkModeKit.m index dd9f154..289e6f3 100644 --- a/Sources/DarkModeCore/UIImage+DarkModeKit.m +++ b/Sources/DarkModeCore/UIImage+DarkModeKit.m @@ -5,14 +5,34 @@ #import "UIImage+DarkModeKit.h" #import "DMDynamicImage.h" -#import "NSObject+DarkModeKit.h" @import ObjectiveC; @implementation UIImage (DarkModeKit) -+ (void)load { - [UIImage dm_swizzleInstanceMethod:@selector(isEqual:) to:@selector(dm_isEqual:)]; ++ (void)dm_swizzleIsEqual { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + SEL selector = @selector(isEqual:); + Method method = class_getInstanceMethod(self, selector); + if (!method) + NSAssert(NO, @"Method not found for [UIImage isEqual:]"); + + IMP imp = method_getImplementation(method); + class_replaceMethod(self, selector, imp_implementationWithBlock(^BOOL(UIImage *self, UIImage *other) { + /// On iOS 13, UIImage `isEqual:` somehow changes internally and doesn't work for `NSProxy`, + /// here we forward the message to internal images manually + UIImage *realSelf = self; + UIImage *realOther = other; + if (object_getClass(self) == DMDynamicImageProxy.class) { + realSelf = ((DMDynamicImageProxy *)self).resolvedImage; + } + if (object_getClass(other) == DMDynamicImageProxy.class) { + realOther = ((DMDynamicImageProxy *)other).resolvedImage; + } + return ((BOOL(*)(UIImage *, SEL, UIImage *))imp)(realSelf, selector, realOther); + }), method_getTypeEncoding(method)); + }); } + (UIImage *)dm_imageWithLightImage:(UIImage *)lightImage darkImage:(UIImage *)darkImage { @@ -25,18 +45,4 @@ return [UIImage dm_imageWithLightImage:lightImage darkImage:darkImage]; } -- (BOOL)dm_isEqual:(UIImage *)other { - /// On iOS 13, UIImage `isEqual:` somehow changes internally and doesn't work for `NSProxy`, - /// here we forward the message to internal images manually - UIImage *realSelf = self; - UIImage *realOther = other; - if (object_getClass(self) == DMDynamicImageProxy.class) { - realSelf = ((DMDynamicImageProxy *)self).resolvedImage; - } - if (object_getClass(other) == DMDynamicImageProxy.class) { - realOther = ((DMDynamicImageProxy *)other).resolvedImage; - } - return [realSelf dm_isEqual:realOther]; -} - @end diff --git a/Sources/DarkModeCore/UIView+DarkModeKit.m b/Sources/DarkModeCore/UIView+DarkModeKit.m index 2baf65c..154ccac 100644 --- a/Sources/DarkModeCore/UIView+DarkModeKit.m +++ b/Sources/DarkModeCore/UIView+DarkModeKit.m @@ -10,24 +10,24 @@ @implementation UIView (DarkModeKit) -static void (*dm_original_setBackgroundColor)(UIView *, SEL, UIColor *); - -static void dm_setBackgroundColor(UIView *self, SEL _cmd, UIColor *color) { - if ([color isKindOfClass:[DMDynamicColor class]]) { - self.dm_dynamicBackgroundColor = (DMDynamicColor *)color; - } else { - self.dm_dynamicBackgroundColor = nil; - } - dm_original_setBackgroundColor(self, _cmd, color); -} - -// https://stackoverflow.com/questions/42677534/swizzling-on-properties-that-conform-to-ui-appearance-selector + (void)dm_swizzleSetBackgroundColor { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - Method method = class_getInstanceMethod(self, @selector(setBackgroundColor:)); - dm_original_setBackgroundColor = (void *)method_getImplementation(method); - method_setImplementation(method, (IMP)dm_setBackgroundColor); + SEL selector = @selector(setBackgroundColor:); + Method method = class_getInstanceMethod(self, selector); + if (!method) + NSAssert(NO, @"Method not found for [UIView setBackgroundColor:]"); + + IMP imp = method_getImplementation(method); + class_replaceMethod(self, selector, imp_implementationWithBlock(^(UIView *self, UIColor *backgroundColor) { + if ([backgroundColor isKindOfClass:[DMDynamicColor class]]) { + self.dm_dynamicBackgroundColor = (DMDynamicColor *)backgroundColor; + } + else { + self.dm_dynamicBackgroundColor = nil; + } + ((void (*)(UIView *, SEL, UIColor *))imp)(self, selector, backgroundColor); + }), method_getTypeEncoding(method)); }); } diff --git a/Sources/DarkModeCore/include/DarkModeCore.h b/Sources/DarkModeCore/include/DarkModeCore.h index 4cfb270..2b3ccbe 100644 --- a/Sources/DarkModeCore/include/DarkModeCore.h +++ b/Sources/DarkModeCore/include/DarkModeCore.h @@ -6,7 +6,6 @@ #import "../DMDynamicImage.h" #import "../DMNamespace.h" #import "../DMTraitCollection.h" -#import "../NSObject+DarkModeKit.h" #import "../UIColor+DarkModeKit.h" #import "../UIImage+DarkModeKit.h" #import "../UIView+DarkModeKit.h" diff --git a/Sources/FluentDarkModeKit/DarkModeManager.swift b/Sources/FluentDarkModeKit/DarkModeManager.swift index 956b1ac..052e5bd 100644 --- a/Sources/FluentDarkModeKit/DarkModeManager.swift +++ b/Sources/FluentDarkModeKit/DarkModeManager.swift @@ -18,6 +18,7 @@ public final class DarkModeManager: NSObject { UILabel.swizzleDidMoveToWindowOnce // Images + UIImage.dm_swizzleIsEqual() UIImageView.swizzleSetImageOnce UIImageView.swizzleInitImageOnce UITabBarItem.swizzleSetImageOnce diff --git a/Sources/FluentDarkModeKit/Extensions/UIImageView+DarkModeKit.swift b/Sources/FluentDarkModeKit/Extensions/UIImageView+DarkModeKit.swift index 7336c4b..64060ce 100644 --- a/Sources/FluentDarkModeKit/Extensions/UIImageView+DarkModeKit.swift +++ b/Sources/FluentDarkModeKit/Extensions/UIImageView+DarkModeKit.swift @@ -15,23 +15,41 @@ extension UIImageView { } static let swizzleSetImageOnce: Void = { - if !dm_swizzleInstanceMethod(#selector(setter: image), to: #selector(dm_setImage(_:))) { - assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UIImageView.self, selector: #selector(setter: image))) + let selector = #selector(setter: image) + guard let method = class_getInstanceMethod(UIImageView.self, selector) else { + assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UIImageView.self, selector: selector)) + return } + + let imp = method_getImplementation(method) + class_replaceMethod(UIImageView.self, selector, imp_implementationWithBlock({ (self: UIImageView, image: UIImage?) -> Void in + if object_getClass(image) == DMDynamicImageProxy.self { + self.dm_dynamicImage = image + } + else { + self.dm_dynamicImage = nil + } + let oldIMP = unsafeBitCast(imp, to: (@convention(c) (UIImageView, Selector, UIImage?) -> Void).self) + oldIMP(self, selector, image) + } as @convention(block) (UIImageView, UIImage?) -> Void), method_getTypeEncoding(method)) }() static let swizzleInitImageOnce: Void = { - if !dm_swizzleInstanceMethod(#selector(UIImageView.init(image:)), to: #selector(UIImageView.dm_init(image:))) { - assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UIImageView.self, selector: #selector(setter: image))) + let selector = #selector(UIImageView.init(image:)) + guard let method = class_getInstanceMethod(UIImageView.self, selector) else { + assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UIImageView.self, selector: selector)) + return } - }() - @objc dynamic func dm_init(image: UIImage?) -> UIImageView { - if object_getClass(image) == DMDynamicImageProxy.self { - dm_dynamicImage = image - } - return dm_init(image: image) - } + let imp = method_getImplementation(method) + class_replaceMethod(UIImageView.self, selector, imp_implementationWithBlock({ (self: UIImageView, image: UIImage?) -> UIImageView in + if object_getClass(image) == DMDynamicImageProxy.self { + self.dm_dynamicImage = image + } + let oldIMP = unsafeBitCast(imp, to: (@convention(c) (UIImageView, Selector, UIImage?) -> UIImageView).self) + return oldIMP(self, selector, image) + } as @convention(block) (UIImageView, UIImage?) -> UIImageView), method_getTypeEncoding(method)) + }() override func dm_updateDynamicImages() { super.dm_updateDynamicImages() @@ -40,14 +58,4 @@ extension UIImageView { image = dynamicImage } } - - @objc dynamic func dm_setImage(_ image: UIImage?) { - if object_getClass(image) == DMDynamicImageProxy.self { - dm_dynamicImage = image - } - else { - dm_dynamicImage = nil - } - dm_setImage(image) - } } diff --git a/Sources/FluentDarkModeKit/Extensions/UILabel+DarkModeKit.swift b/Sources/FluentDarkModeKit/Extensions/UILabel+DarkModeKit.swift index 97033b4..a10036f 100644 --- a/Sources/FluentDarkModeKit/Extensions/UILabel+DarkModeKit.swift +++ b/Sources/FluentDarkModeKit/Extensions/UILabel+DarkModeKit.swift @@ -9,9 +9,21 @@ extension UILabel { } static let swizzleDidMoveToWindowOnce: Void = { - if !dm_swizzleInstanceMethod(#selector(didMoveToWindow), to: #selector(dm_didMoveToWindow)) { - assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UILabel.self, selector: #selector(didMoveToWindow))) + let selector = #selector(didMoveToWindow) + guard let method = class_getInstanceMethod(UILabel.self, selector) else { + assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UILabel.self, selector: selector)) + return } + + let imp = method_getImplementation(method) + class_replaceMethod(UILabel.self, selector, imp_implementationWithBlock({ (self: UILabel) -> Void in + let oldIMP = unsafeBitCast(imp, to: (@convention(c) (UILabel, Selector) -> Void).self) + oldIMP(self, selector) + if self.currentUserInterfaceStyle != DMTraitCollection.current.userInterfaceStyle { + self.currentUserInterfaceStyle = DMTraitCollection.current.userInterfaceStyle + self.dmTraitCollectionDidChange(nil) + } + } as @convention(block) (UILabel) -> Void), method_getTypeEncoding(method)) }() private var currentUserInterfaceStyle: DMUserInterfaceStyle? { @@ -19,16 +31,14 @@ extension UILabel { set { objc_setAssociatedObject(self, &Constants.currentThemeKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) } } - @objc private dynamic func dm_didMoveToWindow() { - dm_didMoveToWindow() - if currentUserInterfaceStyle != DMTraitCollection.current.userInterfaceStyle { - currentUserInterfaceStyle = DMTraitCollection.current.userInterfaceStyle - dmTraitCollectionDidChange(nil) - } - } - override open func dmTraitCollectionDidChange(_ previousTraitCollection: DMTraitCollection?) { super.dmTraitCollectionDidChange(previousTraitCollection) + + guard #available(iOS 12.0, *) else { + // Fix for iOS 11.x + updateDynamicColorInAttributedText() + return + } } private func updateDynamicColorInAttributedText() { diff --git a/Sources/FluentDarkModeKit/Extensions/UITabBar+DarkModeKit.swift b/Sources/FluentDarkModeKit/Extensions/UITabBar+DarkModeKit.swift index 23d247c..549ffd4 100644 --- a/Sources/FluentDarkModeKit/Extensions/UITabBar+DarkModeKit.swift +++ b/Sources/FluentDarkModeKit/Extensions/UITabBar+DarkModeKit.swift @@ -33,15 +33,43 @@ extension UITabBarItem: DMTraitEnvironment { } static let swizzleSetImageOnce: Void = { - if !dm_swizzleInstanceMethod(#selector(setter: image), to: #selector(dm_setImage(_:))) { - assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UITabBarItem.self, selector: #selector(setter: image))) + let selector = #selector(setter: image) + guard let method = class_getInstanceMethod(UITabBarItem.self, selector) else { + assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UITabBarItem.self, selector: selector)) + return } + + let imp = method_getImplementation(method) + class_replaceMethod(UITabBarItem.self, selector, imp_implementationWithBlock({ (self: UITabBarItem, image: UIImage?) -> Void in + if object_getClass(image) == DMDynamicImageProxy.self { + self.dm_dynamicImage = image + } + else { + self.dm_dynamicImage = nil + } + let oldIMP = unsafeBitCast(imp, to: (@convention(c) (UITabBarItem, Selector, UIImage?) -> Void).self) + oldIMP(self, selector, image) + } as @convention(block) (UITabBarItem, UIImage?) -> Void), method_getTypeEncoding(method)) }() static let swizzleSetSelectedImageOnce: Void = { - if !dm_swizzleInstanceMethod(#selector(setter: selectedImage), to: #selector(dm_setSelectedImage(_:))) { - assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UITabBarItem.self, selector: #selector(setter: selectedImage))) + let selector = #selector(setter: selectedImage) + guard let method = class_getInstanceMethod(UITabBarItem.self, selector) else { + assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UITabBarItem.self, selector: selector)) + return } + + let imp = method_getImplementation(method) + class_replaceMethod(UITabBarItem.self, selector, imp_implementationWithBlock({ (self: UITabBarItem, image: UIImage?) -> Void in + if object_getClass(image) == DMDynamicImageProxy.self { + self.dm_dynamicSelectedImage = image + } + else { + self.dm_dynamicSelectedImage = nil + } + let oldIMP = unsafeBitCast(imp, to: (@convention(c) (UITabBarItem, Selector, UIImage?) -> Void).self) + oldIMP(self, selector, image) + } as @convention(block) (UITabBarItem, UIImage?) -> Void), method_getTypeEncoding(method)) }() open func dmTraitCollectionDidChange(_ previousTraitCollection: DMTraitCollection?) { @@ -58,24 +86,4 @@ extension UITabBarItem: DMTraitEnvironment { } } - @objc dynamic func dm_setImage(_ image: UIImage?) { - if object_getClass(image) == DMDynamicImageProxy.self { - dm_dynamicImage = image - } - else { - dm_dynamicImage = nil - } - dm_setImage(image) - } - - @objc dynamic func dm_setSelectedImage(_ image: UIImage?) { - if object_getClass(image) == DMDynamicImageProxy.self { - dm_dynamicSelectedImage = image - } - else { - dm_dynamicSelectedImage = nil - } - dm_setSelectedImage(image) - } - } diff --git a/Sources/FluentDarkModeKit/Extensions/UITextField+DarkModeKit.swift b/Sources/FluentDarkModeKit/Extensions/UITextField+DarkModeKit.swift index 3725a10..58b915c 100644 --- a/Sources/FluentDarkModeKit/Extensions/UITextField+DarkModeKit.swift +++ b/Sources/FluentDarkModeKit/Extensions/UITextField+DarkModeKit.swift @@ -26,16 +26,20 @@ extension UITextField { /// `UITextField` will not call `super.willMove(toWindow:)` in its implementation, so we need to swizzle it separately. static let swizzleTextFieldWillMoveToWindowOnce: Void = { - if !dm_swizzleInstanceMethod(#selector(willMove(toWindow:)), to: #selector(dm_textFieldWillMove(toWindow:))) { - assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UITextField.self, selector: #selector(willMove(toWindow:)))) + let selector = #selector(willMove(toWindow:)) + guard let method = class_getInstanceMethod(UITextField.self, selector) else { + assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UITextField.self, selector: selector)) + return } - }() - @objc private dynamic func dm_textFieldWillMove(toWindow window: UIWindow?) { - dm_textFieldWillMove(toWindow: window) - if window != nil { - dm_updateDynamicColors() - dm_updateDynamicImages() - } - } + let imp = method_getImplementation(method) + class_replaceMethod(UITextField.self, selector, imp_implementationWithBlock({ (self: UITextField, window: UIWindow?) -> Void in + let oldIMP = unsafeBitCast(imp, to: (@convention(c) (UITextField, Selector, UIWindow?) -> Void).self) + oldIMP(self, selector, window) + if window != nil { + self.dm_updateDynamicColors() + self.dm_updateDynamicImages() + } + } as @convention(block) (UITextField, UIWindow?) -> Void), method_getTypeEncoding(method)) + }() } diff --git a/Sources/FluentDarkModeKit/Extensions/UIView+DarkModeKit.swift b/Sources/FluentDarkModeKit/Extensions/UIView+DarkModeKit.swift index 84a00cd..28bab64 100644 --- a/Sources/FluentDarkModeKit/Extensions/UIView+DarkModeKit.swift +++ b/Sources/FluentDarkModeKit/Extensions/UIView+DarkModeKit.swift @@ -28,18 +28,22 @@ extension UIView: DMTraitEnvironment { extension UIView { static let swizzleWillMoveToWindowOnce: Void = { - if !dm_swizzleInstanceMethod(#selector(willMove(toWindow:)), to: #selector(dm_willMove(toWindow:))) { - assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UIView.self, selector: #selector(willMove(toWindow:)))) + let selector = #selector(willMove(toWindow:)) + guard let method = class_getInstanceMethod(UIView.self, selector) else { + assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UIView.self, selector: selector)) + return } - }() - @objc private dynamic func dm_willMove(toWindow window: UIWindow?) { - dm_willMove(toWindow: window) - if window != nil { - dm_updateDynamicColors() - dm_updateDynamicImages() - } - } + let imp = method_getImplementation(method) + class_replaceMethod(UIView.self, selector, imp_implementationWithBlock({ (self: UIView, window: UIWindow?) -> Void in + let oldIMP = unsafeBitCast(imp, to: (@convention(c) (UIView, Selector, UIWindow?) -> Void).self) + oldIMP(self, selector, window) + if window != nil { + self.dm_updateDynamicColors() + self.dm_updateDynamicImages() + } + } as @convention(block) (UIView, UIWindow?) -> Void), method_getTypeEncoding(method)) + }() } extension UIView { @@ -48,18 +52,22 @@ extension UIView { } static let swizzleSetTintColorOnce: Void = { - if !dm_swizzleInstanceMethod(#selector(setter: tintColor), to: #selector(dm_setTintColor)) { - assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UIView.self, selector: #selector(setter: tintColor))) + let selector = #selector(setter: tintColor) + guard let method = class_getInstanceMethod(UIView.self, selector) else { + assertionFailure(DarkModeManager.messageForSwizzlingFailed(class: UIView.self, selector: selector)) + return } + + let imp = method_getImplementation(method) + class_replaceMethod(UIView.self, selector, imp_implementationWithBlock({ (self: UIView, tintColor: UIColor) -> Void in + self.dm_dynamicTintColor = tintColor as? DynamicColor + let oldIMP = unsafeBitCast(imp, to: (@convention(c) (UIView, Selector, UIColor) -> Void).self) + oldIMP(self, selector, tintColor) + } as @convention(block) (UIView, UIColor) -> Void), method_getTypeEncoding(method)) }() private var dm_dynamicTintColor: DynamicColor? { get { return objc_getAssociatedObject(self, &Constants.dynamicTintColorKey) as? DynamicColor } set { objc_setAssociatedObject(self, &Constants.dynamicTintColorKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) } } - - @objc private dynamic func dm_setTintColor(_ color: UIColor) { - dm_dynamicTintColor = color as? DynamicColor - dm_setTintColor(color) - } } diff --git a/Sources/FluentDarkModeKitExample/MainViewController.swift b/Sources/FluentDarkModeKitExample/MainViewController.swift index 5e6758d..0675612 100644 --- a/Sources/FluentDarkModeKitExample/MainViewController.swift +++ b/Sources/FluentDarkModeKitExample/MainViewController.swift @@ -15,6 +15,7 @@ final class MainViewController: ViewController { Row(name: "UIView", vcType: UIViewVC.self), Row(name: "UIActivityIndicatorView", vcType: UIActivityIndicatorViewVC.self), Row(name: "UIButton", vcType: UIButtonVC.self), + Row(name: "UILabel", vcType: UILabelVC.self), Row(name: "UIPageControl", vcType: UIPageControlVC.self) ] diff --git a/Sources/FluentDarkModeKitExample/UILabelVC.swift b/Sources/FluentDarkModeKitExample/UILabelVC.swift new file mode 100644 index 0000000..b28f90a --- /dev/null +++ b/Sources/FluentDarkModeKitExample/UILabelVC.swift @@ -0,0 +1,30 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// + +import FluentDarkModeKit + +final class UILabelVC: ViewController { + let label: UILabel = { + let label = UILabel() + label.text = "Test" + label.textColor = UIColor(.dm, light: .red, dark: .green) + return label + }() + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = UIColor(.dm, light: .white, dark: .black) + + view.addSubview(label) + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + + label.sizeToFit() + label.center = view.center + } +}