QMUI_iOS
QMUI_iOS copied to clipboard
QMUITheme 切换时无法刷新 UITabBar/UINavigationBar 的 barStyle、backgroundImage 等样式
考虑下面这种页面结构:
一旦在 visibleViewControlle
触发 dark mode 之后,QMUIConfiguration
如下属性都可能改变:
@property(nonatomic, strong, nullable) UIImage *tabBarBackgroundImage;
@property(nonatomic, strong, nullable) UIColor *tabBarBarTintColor;
@property(nonatomic, strong, nullable) UIColor *tabBarShadowImageColor;
@property(nonatomic, assign) UIBarStyle tabBarStyle;
@property(nonatomic, strong, nullable) UIFont *tabBarItemTitleFont;
@property(nonatomic, strong, nullable) UIColor *tabBarItemTitleColor;
@property(nonatomic, strong, nullable) UIColor *tabBarItemTitleColorSelected;
@property(nonatomic, strong, nullable) UIColor *tabBarItemImageColor;
@property(nonatomic, strong, nullable) UIColor *tabBarItemImageColorSelected;
拿 tabBarStyle
举例:
- (void)setTabBarStyle:(UIBarStyle)tabBarStyle {
_tabBarStyle = tabBarStyle;
#ifdef IOS13_SDK_ALLOWED
if (@available(iOS 13.0, *)) {
self.tabBarAppearance.backgroundEffect = [UIBlurEffect effectWithStyle:tabBarStyle == UIBarStyleDefault ? UIBlurEffectStyleSystemChromeMaterialLight : UIBlurEffectStyleSystemChromeMaterialDark];
[self updateTabBarAppearance];
} else {
#endif
[UITabBar appearance].barStyle = tabBarStyle;
[self.appearanceUpdatingTabBarControllers enumerateObjectsUsingBlock:^(UITabBarController * _Nonnull tabBarController, NSUInteger idx, BOOL * _Nonnull stop) {
tabBarController.tabBar.barStyle = tabBarStyle;
}];
#ifdef IOS13_SDK_ALLOWED
}
#endif
}
此时的self.appearanceUpdatingTabBarControllers
是空的,因为如下方式是拿不到我要的UITabbarController
的:
- (NSArray <UIViewController *>*)qmui_existingViewControllersOfClass:(Class)class {
NSMutableSet *viewControllers = [NSMutableSet set];
...
if ([self isKindOfClass:UINavigationController.class]) {
[viewControllers addObjectsFromArray:[((UINavigationController *)self).visibleViewController qmui_existingViewControllersOfClass:class]];
}
if ([self isKindOfClass:UITabBarController.class]) {
[viewControllers addObjectsFromArray:[((UITabBarController *)self).selectedViewController qmui_existingViewControllersOfClass:class]];
}
....
return viewControllers.allObjects;
}
因此只能在VC1
内通过qmui_themeDidChangeByManager
进行手动修改
- (void)qmui_themeDidChangeByManager:(QMUIThemeManager *)manager identifier:(__kindof NSObject<NSCopying> *)identifier theme:(__kindof NSObject *)theme
{
[super qmui_themeDidChangeByManager:manager identifier:identifier theme:theme];
....
// 需要手动刷新样式
UIImage *tabBarBackgroundImage = [identifier isEqualToString:WEThemeIdentifierDark] ? [[UIImage qmui_imageWithColor:UIColorMake(249, 249, 249)] resizableImageWithCapInsets:UIEdgeInsetsMake(1, 1, 1, 1)] : nil;
UIColor *tabBarBarTintColor = nil;
UIColor *tabBarShadowImageColor = UIColorSeparator;
UIBarStyle tabBarStyle = [identifier isEqualToString:WEThemeIdentifierDark] ? UIBarStyleBlack : UIBarStyleDefault;
UIFont *tabBarItemTitleFont = nil;
UIColor *tabBarItemTitleColor = UIColorGray6;
UIColor *tabBarItemTitleColorSelected = DefaultThemeColor;
UIColor *tabBarItemImageColor = UIColorGray6;
UIColor *tabBarItemImageColorSelected = DefaultThemeColor;
self.tabBar.backgroundImage = tabBarBackgroundImage;
if (@available(iOS 13.0, *)) {
UITabBarAppearance *tabBarAppearance = [[UITabBarAppearance alloc] init];
[tabBarAppearance configureWithDefaultBackground];
tabBarAppearance.backgroundColor = tabBarBarTintColor;
tabBarAppearance.shadowColor = tabBarShadowImageColor;
tabBarAppearance.backgroundEffect = [UIBlurEffect effectWithStyle:tabBarStyle == UIBarStyleDefault ? UIBlurEffectStyleSystemChromeMaterialLight : UIBlurEffectStyleSystemChromeMaterialDark];
[tabBarAppearance qmui_applyItemAppearanceWithBlock:^(UITabBarItemAppearance * _Nonnull itemAppearance) {
NSMutableDictionary<NSAttributedStringKey, id> *attributes = itemAppearance.normal.titleTextAttributes.mutableCopy;
attributes[NSFontAttributeName] = tabBarItemTitleFont;
attributes[NSForegroundColorAttributeName] = tabBarItemTitleColor;
itemAppearance.normal.titleTextAttributes = attributes.copy;
attributes[NSForegroundColorAttributeName] = tabBarItemTitleColorSelected;
itemAppearance.selected.titleTextAttributes = attributes.copy;
itemAppearance.normal.iconColor = tabBarItemImageColor;
itemAppearance.selected.iconColor = tabBarItemImageColorSelected;
}];
self.tabBar.standardAppearance = tabBarAppearance;
} else {
UITabBar.appearance.backgroundImage = tabBarBackgroundImage;
UITabBar.appearance.barTintColor = tabBarBarTintColor;
UITabBar.appearance.shadowImage = [UIImage qmui_imageWithColor:tabBarShadowImageColor size:CGSizeMake(1, PixelOne) cornerRadius:0];
UITabBar.appearance.barStyle = tabBarStyle;
NSMutableDictionary<NSString *, id> *textAttributes = [[NSMutableDictionary alloc] initWithDictionary:[[UITabBarItem appearance] titleTextAttributesForState:UIControlStateNormal]];
if (tabBarItemTitleFont) {
textAttributes[NSFontAttributeName] = tabBarItemTitleFont;
}
if (tabBarItemTitleColor) {
textAttributes[NSForegroundColorAttributeName] = tabBarItemTitleColor;
}
NSMutableDictionary<NSString *, id> *selected_textAttributes = [[NSMutableDictionary alloc] initWithDictionary:[[UITabBarItem appearance] titleTextAttributesForState:UIControlStateSelected]];
if (tabBarItemTitleColorSelected) {
selected_textAttributes[NSForegroundColorAttributeName] = tabBarItemTitleColorSelected;
}
[UITabBarItem.appearance setTitleTextAttributes:textAttributes forState:UIControlStateNormal];
[UITabBarItem.appearance setTitleTextAttributes:selected_textAttributes forState:UIControlStateSelected];
UITabBar.appearance.tintColor = tabBarItemImageColorSelected;
self.tabBar.barStyle = tabBarStyle;
self.tabBar.barTintColor = tabBarBarTintColor;
self.tabBar.shadowImage = [UIImage qmui_imageWithColor:tabBarShadowImageColor size:CGSizeMake(1, PixelOne) cornerRadius:0];
[self.tabBar.items enumerateObjectsUsingBlock:^(UITabBarItem * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj setTitleTextAttributes:textAttributes forState:UIControlStateNormal];
[obj setTitleTextAttributes:selected_textAttributes forState:UIControlStateSelected];
[obj qmui_updateTintColorForiOS12AndEarlier:tabBarItemImageColor];
}];
self.tabBar.tintColor = tabBarItemImageColorSelected;
}
}
需要手动修改的东西非常多,也都是QMUIConfiguration
里复制出来的重复代码...能否将QMUIConfiguration
内对于 UITabbar
修改的代码开放出来,比如抽一个方法到头文件,方便一些特殊页面结构快速修改
+1
QMUITheme 发生变化时,它内置两种方式去更新样式:
- 某些对象的某些属性是支持动态的,例如
UIColor
、UIImage
类型,这种情况会在- [UIView(QMUITheme) qmui_themeDidChangeByManager:identifier:theme:]
里按照QMUIThemePropertiesRegister
提供的属性列表依次调用 setter,以触发外观的刷新。这种方式只支持属性,不支持多参数的方法(例如导航栏的背景图是通过- [UINavigationBar setBackgroundImage:forBarMetrics:]
设置的,这个方法有两个参数,不支持这种方式,所以导航栏的背景图无法通过方法 1 来刷新),也不支持非对象的类型(例如UIBarStyle
)。 - 如果有使用配置表,则在主题发生变化时,配置表也会重新加载,在
QMUIConfiguration
的某些属性的 setter (例如 issue 提到的setTabBarStyle:
)里会获取界面中的特定 View 去主动刷新,但如果这里寻找特定 View 的方式没有命中当前的业务场景,找不到 View,就无法被刷新。
回到 issue 描述的场景里,其实不需要这种特殊的 viewController 结构,只需要将一个普通的 UINavigationBar
或 UITabBar
直接添加到当前界面,然后触发主题切换,就会发现这些 bar 的 backgroundImage
、barStyle
无法被更新——因为这些属性不符合上述的两点,也即非单一属性,或者非对象数据类型,也不匹配配置表里寻找 View 的逻辑。
这应该是 QMUITheme 一个通用的 bug,我们再考虑如何解决,在此之前确实只能按你说的,用很麻烦的方式从配置表里重新读取值再设置上去。