cdts/xdts-ios 3/TreeHole/CYHResetCode/CYH/QMUIKit/QMUIComponents/QMUITheme/UIView+QMUITheme.m

221 lines
12 KiB
Mathematica
Raw Permalink Normal View History

2023-07-27 09:20:00 +08:00
/**
* Tencent is pleased to support the open source community by making QMUI_iOS available.
* Copyright (C) 2016-2021 THL A29 Limited, a Tencent company. All rights reserved.
* Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
* http://opensource.org/licenses/MIT
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
*/
//
// UIView+QMUITheme.m
// QMUIKit
//
// Created by MoLice on 2019/6/21.
//
#import "UIView+QMUITheme.h"
#import "QMUICore.h"
#import "UIView+QMUI.h"
#import "UIColor+QMUI.h"
#import "UIImage+QMUI.h"
#import "UIImage+QMUITheme.h"
#import "UIVisualEffect+QMUITheme.h"
#import "QMUIThemeManagerCenter.h"
#import "CALayer+QMUI.h"
#import "QMUIThemeManager.h"
#import "QMUIThemePrivate.h"
#import "NSObject+QMUI.h"
#import "UITextInputTraits+QMUI.h"
@implementation UIView (QMUITheme)
QMUISynthesizeIdCopyProperty(qmui_themeDidChangeBlock, setQmui_themeDidChangeBlock)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
OverrideImplementation([UIView class], @selector(setHidden:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UIView *selfObject, BOOL firstArgv) {
BOOL valueChanged = selfObject.hidden != firstArgv;
// call super
void (*originSelectorIMP)(id, SEL, BOOL);
originSelectorIMP = (void (*)(id, SEL, BOOL))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv);
if (valueChanged) {
// UIView.qmui_currentThemeIdentifier theme string hidden subviews
[selfObject _qmui_themeDidChangeByManager:nil identifier:nil theme:nil shouldEnumeratorSubviews:YES];
}
};
});
OverrideImplementation([UIView class], @selector(setAlpha:), ^id(__unsafe_unretained Class originClass, SEL originCMD, IMP (^originalIMPProvider)(void)) {
return ^(UIView *selfObject, CGFloat firstArgv) {
BOOL willShow = selfObject.alpha <= 0 && firstArgv > 0.01;
// call super
void (*originSelectorIMP)(id, SEL, CGFloat);
originSelectorIMP = (void (*)(id, SEL, CGFloat))originalIMPProvider();
originSelectorIMP(selfObject, originCMD, firstArgv);
if (willShow) {
// identifier theme
[selfObject _qmui_themeDidChangeByManager:nil identifier:nil theme:nil shouldEnumeratorSubviews:YES];
}
};
});
// class didMoveToWindow super
NSArray<Class> *classes = @[UIView.class,
UICollectionView.class,
UITextField.class,
UISearchBar.class,
NSClassFromString(@"UITableViewLabel")];
if (NSClassFromString(@"WKWebView")) {
classes = [classes arrayByAddingObject:NSClassFromString(@"WKWebView")];
}
[classes enumerateObjectsUsingBlock:^(Class _Nonnull class, NSUInteger idx, BOOL * _Nonnull stop) {
ExtendImplementationOfVoidMethodWithoutArguments(class, @selector(didMoveToWindow), ^(UIView *selfObject) {
// enumerateSubviews NO view didMoveToWindow subview didMoveToWindow subview
if (selfObject.window) {
[selfObject _qmui_themeDidChangeByManager:nil identifier:nil theme:nil shouldEnumeratorSubviews:NO];
}
});
}];
});
}
- (void)qmui_registerThemeColorProperties:(NSArray<NSString *> *)getters {
[getters enumerateObjectsUsingBlock:^(NSString * _Nonnull getterString, NSUInteger idx, BOOL * _Nonnull stop) {
SEL getter = NSSelectorFromString(getterString);
SEL setter = setterWithGetter(getter);
NSString *setterString = NSStringFromSelector(setter);
QMUIAssert([self respondsToSelector:getter], @"UIView (QMUITheme)", @"register theme color fails, %@ does not have method called %@", NSStringFromClass(self.class), getterString);
QMUIAssert([self respondsToSelector:setter], @"UIView (QMUITheme)", @"register theme color fails, %@ does not have method called %@", NSStringFromClass(self.class), setterString);
if (!self.qmuiTheme_themeColorProperties) {
self.qmuiTheme_themeColorProperties = NSMutableDictionary.new;
}
self.qmuiTheme_themeColorProperties[getterString] = setterString;
}];
}
- (void)qmui_unregisterThemeColorProperties:(NSArray<NSString *> *)getters {
if (!self.qmuiTheme_themeColorProperties) return;
[getters enumerateObjectsUsingBlock:^(NSString * _Nonnull getterString, NSUInteger idx, BOOL * _Nonnull stop) {
[self.qmuiTheme_themeColorProperties removeObjectForKey:getterString];
}];
}
- (void)qmui_themeDidChangeByManager:(QMUIThemeManager *)manager identifier:(__kindof NSObject<NSCopying> *)identifier theme:(__kindof NSObject *)theme {
if (![self _qmui_visible]) return;
// view QMUIThemePrivate getter
[self.qmuiTheme_themeColorProperties enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull getterString, NSString * _Nonnull setterString, BOOL * _Nonnull stop) {
SEL getter = NSSelectorFromString(getterString);
SEL setter = NSSelectorFromString(setterString);
// tintColor nil superview tintColor
if (getter == @selector(tintColor)) {
if (!self.qmui_tintColorCustomized) return;
}
// UITabBarItem UITabBarSwappableImageView.image = image selectedImage normal image UITabBarSwappableImageView setImage
// https://github.com/Tencent/QMUI_iOS/issues/1122
if ([self isKindOfClass:NSClassFromString(@"UITabBarSwappableImageView")] && getter == @selector(image)) {
return;
}
// UIColor NSAttributedString UITextField.attributedText
BeginIgnorePerformSelectorLeaksWarning
id value = [self performSelector:getter];
if (!value) return;
BOOL isValidatedColor = [value isKindOfClass:QMUIThemeColor.class] && (!manager || [((QMUIThemeColor *)value).managerName isEqual:manager.name]);
BOOL isValidatedImage = [value isKindOfClass:QMUIThemeImage.class] && (!manager || [((QMUIThemeImage *)value).managerName isEqual:manager.name]);
BOOL isValidatedEffect = [value isKindOfClass:QMUIThemeVisualEffect.class] && (!manager || [((QMUIThemeVisualEffect *)value).managerName isEqual:manager.name]);
BOOL isOtherObject = ![value isKindOfClass:UIColor.class] && ![value isKindOfClass:UIImage.class] && ![value isKindOfClass:UIVisualEffect.class];// colorimageeffect NSAttributedString
if (isOtherObject || isValidatedColor || isValidatedImage || isValidatedEffect) {
[self performSelector:setter withObject:value];
}
EndIgnorePerformSelectorLeaksWarning
}];
// view
// iOS 10-11 UILabel.attributedText 使 setNeedsDisplay range iOS 912-13 UILabel (QMUIThemeCompatibility)
if ([self isKindOfClass:UILabel.class]) {
[self setNeedsDisplay];
}
if ([self isKindOfClass:UITextView.class]) {
UITextView *textView = (UITextView *)self;
if (@available(iOS 16.0, *)) {
// iOS 16 setNeedsDisplay textColor
// textColor typingAttributes[NSForegroundColorAttributeName]
if (textView.textColor.qmui_isQMUIDynamicColor) {
textView.textColor = textView.textColor;
}
} else {
[self setNeedsDisplay];
}
}
//
if (QMUICMIActivated) {
static NSArray<Class> *inputClasses = nil;
if (!inputClasses) inputClasses = @[UITextField.class, UITextView.class, UISearchBar.class];// Class UITextInputTraits(QMUI)
[inputClasses enumerateObjectsUsingBlock:^(Class _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([self isKindOfClass:obj]) {
NSObject<UITextInputTraits> *input = (NSObject<UITextInputTraits> *)self;
if ([input respondsToSelector:@selector(keyboardAppearance)]) {
if (input.keyboardAppearance != KeyboardAppearance && !input.qmui_hasCustomizedKeyboardAppearance) {
input.qmui_keyboardAppearance = KeyboardAppearance;
}
}
*stop = YES;
}
}];
}
/** 2
1. iOS 13 currentTraitCollection.userInterfaceStyle 便 Drak/Light app QMUI layer layer Drak/Light
2. UIView backgroundColor CALayer backgroundColor CALayer
*/
[CALayer qmui_performWithoutAnimation:^{
[self.layer qmui_setNeedsUpdateDynamicStyle];
}];
if (self.qmui_themeDidChangeBlock) {
self.qmui_themeDidChangeBlock();
}
}
@end
@implementation UIView (QMUITheme_Private)
QMUISynthesizeIdStrongProperty(qmuiTheme_themeColorProperties, setQmuiTheme_themeColorProperties)
- (BOOL)_qmui_visible {
BOOL hidden = self.hidden;
if ([self respondsToSelector:@selector(prepareForReuse)]) {
hidden = NO;// UITableViewCell prepareForReuse setHidden:YES setHidden:NO hidden YES visible themeDidChange UITableViewCell
}
return !hidden && self.alpha > 0.01 && self.window;
}
- (void)_qmui_themeDidChangeByManager:(QMUIThemeManager *)manager identifier:(__kindof NSObject<NSCopying> *)identifier theme:(__kindof NSObject *)theme shouldEnumeratorSubviews:(BOOL)shouldEnumeratorSubviews {
[self qmui_themeDidChangeByManager:manager identifier:identifier theme:theme];
if (shouldEnumeratorSubviews) {
[self.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull subview, NSUInteger idx, BOOL * _Nonnull stop) {
[subview _qmui_themeDidChangeByManager:manager identifier:identifier theme:theme shouldEnumeratorSubviews:YES];
}];
}
}
@end