Skip to content

Commit

Permalink
修复 subspec 依赖问题
Browse files Browse the repository at this point in the history
  • Loading branch information
MoLice committed Sep 27, 2019
1 parent 1292c9b commit 7eb3e3b
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 240 deletions.
177 changes: 177 additions & 0 deletions QMUIKit/QMUIComponents/QMUITheme/QMUIThemePrivate.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#import "UIView+QMUI.h"
#import "UISearchBar+QMUI.h"
#import "UITableViewCell+QMUI.h"
#import "CALayer+QMUI.h"

// QMUI classes
#import "QMUIImagePickerCollectionViewCell.h"
Expand Down Expand Up @@ -200,6 +201,21 @@ + (void)load {
};
});
}

OverrideImplementation([UIView class], @selector(setBackgroundColor:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^void(UIView *selfObject, UIColor *color) {

if ([selfObject.backgroundColor isKindOfClass:[QMUIThemeColor class]] || [color isKindOfClass:[QMUIThemeColor class]]) {
// -[UIView setBackgroundColor:] 会同步修改 layer 的 backgroundColor,但它内部又有一个判断条件即:如果参入传入的 color.CGColor 和当前的 self.layr.backgroundColor 一样,就不会重新设置,而如果 layer.backgroundColor 如果关联了 QMUI 的动态色,忽略这个设置,就会导致前后不一致的问题,这里要强制把 layer.backgroundColor 清空,让每次都调用 -[CALayer setBackgroundColor:] 方法
selfObject.layer.backgroundColor = nil;
}

void (*originSelectorIMP)(id, SEL, UIColor *);
originSelectorIMP = (void (*)(id, SEL, UIColor *))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, color);

};
});
});
}

Expand Down Expand Up @@ -419,3 +435,164 @@ + (void)load {
}

@end

@interface CALayer ()

@property(nonatomic, strong) UIColor *qcl_originalBackgroundColor;
@property(nonatomic, strong) UIColor *qcl_originalBorderColor;
@property(nonatomic, strong) UIColor *qcl_originalShadowColor;

@end

@implementation CALayer (QMUIThemeCompatibility)

QMUISynthesizeIdStrongProperty(qcl_originalBackgroundColor, setQcl_originalBackgroundColor)
QMUISynthesizeIdStrongProperty(qcl_originalBorderColor, setQcl_originalBorderColor)
QMUISynthesizeIdStrongProperty(qcl_originalShadowColor, setQcl_originalShadowColor)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
OverrideImplementation([CALayer class], @selector(setBackgroundColor:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(CALayer *selfObject, CGColorRef color) {

// iOS 13 的 UIDynamicProviderColor,以及 QMUIThemeColor 在获取 CGColor 时会将自身绑定到 CGColorRef 上,这里把原始的 color 重新获取出来存到 property 里,以备样式更新时调用
UIColor *originalColor = [(__bridge id)(color) qmui_getBoundObjectForKey:QMUICGColorOriginalColorBindKey];
selfObject.qcl_originalBackgroundColor = originalColor;

// call super
void (*originSelectorIMP)(id, SEL, CGColorRef);
originSelectorIMP = (void (*)(id, SEL, CGColorRef))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, color);
};
});

OverrideImplementation([CALayer class], @selector(setBorderColor:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(CALayer *selfObject, CGColorRef color) {

UIColor *originalColor = [(__bridge id)(color) qmui_getBoundObjectForKey:QMUICGColorOriginalColorBindKey];
selfObject.qcl_originalBorderColor = originalColor;

// call super
void (*originSelectorIMP)(id, SEL, CGColorRef);
originSelectorIMP = (void (*)(id, SEL, CGColorRef))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, color);
};
});

OverrideImplementation([CALayer class], @selector(setShadowColor:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(CALayer *selfObject, CGColorRef color) {

UIColor *originalColor = [(__bridge id)(color) qmui_getBoundObjectForKey:QMUICGColorOriginalColorBindKey];
selfObject.qcl_originalShadowColor = originalColor;

// call super
void (*originSelectorIMP)(id, SEL, CGColorRef);
originSelectorIMP = (void (*)(id, SEL, CGColorRef))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, color);
};
});

// iOS 13 下,如果系统的主题发生变化,会自动调用每个 view 的 layoutSubviews,所以我们在这里面自动更新样式
// 如果是 QMUIThemeManager 引发的主题变化,会在 theme 那边主动调用 qmui_setNeedsUpdateDynamicStyle,就不依赖这里
if (@available(iOS 13.0, *)) {
ExtendImplementationOfVoidMethodWithoutArguments([UIView class], @selector(layoutSubviews), ^(UIView *selfObject) {
[selfObject.layer qmui_setNeedsUpdateDynamicStyle];
});
}
});
}

- (void)qmui_setNeedsUpdateDynamicStyle {
if (self.qcl_originalBackgroundColor) {
UIColor *originalColor = self.qcl_originalBackgroundColor;
self.backgroundColor = originalColor.CGColor;
}
if (self.qcl_originalBorderColor) {
self.borderColor = self.qcl_originalBorderColor.CGColor;
}
if (self.qcl_originalShadowColor) {
self.shadowColor = self.qcl_originalShadowColor.CGColor;
}

[self.sublayers enumerateObjectsUsingBlock:^(__kindof CALayer * _Nonnull sublayer, NSUInteger idx, BOOL * _Nonnull stop) {
if (!sublayer.qmui_isRootLayerOfView) {// 如果是 UIView 的 rootLayer,它会依赖 UIView 树自己的 layoutSubviews 去逐个触发,不需要手动遍历到,这里只需要遍历那些额外添加到 layer 上的 sublayer 即可
[sublayer qmui_setNeedsUpdateDynamicStyle];
}
}];
}

@end

@interface UISearchBar ()

@property(nonatomic, readonly) NSMutableDictionary <NSString * ,NSInvocation *>*qmuiTheme_invocations;

@end

@implementation UISearchBar (QMUIThemeCompatibility)

+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{

OverrideImplementation([UISearchBar class], @selector(setSearchFieldBackgroundImage:forState:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {

NSMethodSignature *methodSignature = [originClass instanceMethodSignatureForSelector:originCMD];

return ^(UISearchBar *selfObject, UIImage *image, UIControlState state) {

void (*originSelectorIMP)(id, SEL, UIImage *, UIControlState);
originSelectorIMP = (void (*)(id, SEL, UIImage *, UIControlState))originalIMPProvider();

UIImage *previousImage = [selfObject searchFieldBackgroundImageForState:state];
if (previousImage.qmui_isDynamicImage || image.qmui_isDynamicImage) {
// setSearchFieldBackgroundImage:forState: 的内部实现原理:
// 执行后将 image 先存起来,在 layout 时会调用 -[UITextFieldBorderView setImage:] 该方法内部有一个判断:
// if (UITextFieldBorderView._image == image) return
// 由于 QMUIDynamicImage 随时可能发生图片的改变,这里要绕过这个判断:必须先清空一下 image,并马上调用 layoutIfNeeded 触发 -[UITextFieldBorderView setImage:] 使得 UITextFieldBorderView 内部的 image 清空,这样再设置新的才会生效。
originSelectorIMP(selfObject, originCMD, UIImage.new, state);
[selfObject.qmui_textField setNeedsLayout];
[selfObject.qmui_textField layoutIfNeeded];
}
originSelectorIMP(selfObject, originCMD, image, state);

NSInvocation *invocation = nil;
NSString *invocationActionKey = [NSString stringWithFormat:@"%@-%zd", NSStringFromSelector(originCMD), state];
if (image.qmui_isDynamicImage) {
invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
[invocation setSelector:originCMD];
[invocation setArgument:&image atIndex:2];
[invocation setArgument:&state atIndex:3];
[invocation retainArguments];
}
selfObject.qmuiTheme_invocations[invocationActionKey] = invocation;
};
});

});
}

- (void)_qmui_themeDidChangeByManager:(QMUIThemeManager *)manager identifier:(__kindof NSObject<NSCopying> *)identifier theme:(__kindof NSObject *)theme shouldEnumeratorSubviews:(BOOL)shouldEnumeratorSubviews {
[super _qmui_themeDidChangeByManager:manager identifier:identifier theme:theme shouldEnumeratorSubviews:shouldEnumeratorSubviews];
[self qmuiTheme_performUpdateInvocations];
}

- (void)qmuiTheme_performUpdateInvocations {
[[self.qmuiTheme_invocations allValues] enumerateObjectsUsingBlock:^(NSInvocation * _Nonnull invocation, NSUInteger idx, BOOL * _Nonnull stop) {
[invocation setTarget:self];
[invocation invoke];
}];
}


- (NSMutableDictionary *)qmuiTheme_invocations {
NSMutableDictionary *qmuiTheme_invocations = objc_getAssociatedObject(self, _cmd);
if (!qmuiTheme_invocations) {
qmuiTheme_invocations = [NSMutableDictionary dictionary];
objc_setAssociatedObject(self, _cmd, qmuiTheme_invocations, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return qmuiTheme_invocations;
}

@end
24 changes: 0 additions & 24 deletions QMUIKit/QMUIComponents/QMUITheme/UISearchBar+QMUITheme.h

This file was deleted.

98 changes: 0 additions & 98 deletions QMUIKit/QMUIComponents/QMUITheme/UISearchBar+QMUITheme.m

This file was deleted.

4 changes: 2 additions & 2 deletions QMUIKit/QMUIKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,8 @@ static NSString * const QMUI_VERSION = @"4.0.0";
#import "UISearchBar+QMUI.h"
#endif

#if __has_include("UISearchBar+QMUITheme.h")
#import "UISearchBar+QMUITheme.h"
#if __has_include("UISearchController+QMUI.h")
#import "UISearchController+QMUI.h"
#endif

#if __has_include("UISwitch+QMUI.h")
Expand Down
Loading

0 comments on commit 7eb3e3b

Please sign in to comment.