1、前言
2、使用说明
-
2.1 什么控件能使用
- 遵循UIAppearance协议的类,才能使用协议中的方法。
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIBarItem : NSObject <NSCoding, UIAppearance>
NS_CLASS_AVAILABLE_IOS(2_0) @interface UIView : UIResponder <NSCoding, UIAppearance, UIAppearanceContainer, UIDynamicItem, UITraitEnvironment, UICoordinateSpace, UIFocusItem, CALayerDelegate>
- 实际上能使用appearance的地方是在方法或者属性后面有UI_APPEARANCE_SELECTOR宏的地方
@property(nonatomic,assign) UIBarStyle barStyle UI_APPEARANCE_SELECTOR
- (void)setTitleTextAttributes:(nullable NSDictionary<NSString *,id> *)attributes forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;
-
2.2 修改系统默认参数
// UITableView不显示垂直线
[[UITableView appearance] setShowsVerticalScrollIndicator:NO];
// 全部按钮显示白色
[[UIButton appearance] setBackgroundColor:[UIColor whiteColor]];
-
2.3 模仿系统做法
- 如果我们自定义的视图也想要一个全局的外观设置,那么使用UIAppearance来实现非常的方便,接下来就以一个小demo实现。
- 自定义一个继承自UIView的CardView,CardView中添加两个SubView:leftView和rightView,高度和CardView一样,宽度分别占据一半。
- 然后在.h文件中提供修改两个子视图颜色的API,并添加UI_APPEARANCE_SELECTOR宏
@property (nonatomic, strong)UIColor * leftColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)UIColor * rightColor UI_APPEARANCE_SELECTOR;
- 在.m文件中重写他们的setter方法设置两个子视图的颜色
- (void)setLeftColor:(UIColor *)leftColor {
_leftColor = leftColor;
self.leftView.backgroundColor = _leftColor;
}
- (void)setRightColor:(UIColor *)rightColor {
_rightColor = rightColor;
self.rightView.backgroundColor = _rightColor;
}
- 提供两个VC,在第一个VC的viewDidLoad方法中进行全局的颜色设置
- (void)viewDidLoad {
[super viewDidLoad];
[CardView appearance].leftColor = [UIColor redColor];
[CardView appearance].rightColor = [UIColor yellowColor];
}
- 分别在两个VC的touchesBegan方法中初始化和添加CardView视图
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
CardView * cardView = [[CardView alloc]initWithFrame:CGRectMake(20, 100, 200, 100)];
[self.view addSubview:cardView];
}
- 然后运行之后发现两个VC中的CardView的颜色效果是相同的。
![](https://img2023.cnblogs.com/blog/1213778/202211/1213778-20221130174000278-711953801.webp)
![](https://img2023.cnblogs.com/blog/1213778/202211/1213778-20221130174004912-357695637.webp)
-
2.4 根据场景进行定制
-
自定义演示
- UIAppearance既可以修改某一类型控件的全部实例,又可以修改部分实例。
- 比如之前我们在demo中的第一个界面改变CardView的leftColor的全部实例的时候是这样做的
[CardView appearance].leftColor = [UIColor redColor];
+ (instancetype)appearance;
+ (instancetype)appearanceWhenContainedInInstancesOfClasses:(NSArray<Class <UIAppearanceContainer>> *)containerTypes NS_AVAILABLE_IOS(9_0);
- 比如如果第二个VC是以presentViewController的方式跳转的,只想修改第一个界面上的CardView的leftColor可以在上述代码后面增加如下代码:
[CardView appearanceWhenContainedInInstancesOfClasses:@[[UINavigationController class]]].leftColor = [UIColor greenColor];
- 运行之后第一个界面的效果为:
![](https://img2023.cnblogs.com/blog/1213778/202211/1213778-20221130173843192-1813505926.webp)
- 第二个则没有变化。
-
修改系统的
// 获取TZImagePickerController 中的UIBarButtonItem类控件,设置
UIBarButtonItem *barItem;
if (@available(iOS 9.0, *)) {
barItem = [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[TZImagePickerController class]]];
}
else {
barItem = [UIBarButtonItem appearanceWhenContainedIn:[TZImagePickerController class], nil];
}
NSMutableDictionary *textAttrs = [NSMutableDictionary dictionary];
textAttrs[NSForegroundColorAttributeName] = self.barItemTextColor;
textAttrs[NSFontAttributeName] = self.barItemTextFont;
[barItem setTitleTextAttributes:textAttrs forState:UIControlStateNormal];
3、使用注意
- 在通过appearance设置属性的时候,并不会生成实例,立即赋值,而需要视图被加到视图tree中的时候才会在实例生效。
- 所以使用 UIAppearance 只有在视图添加到 window 时才会生效,对于已经在 window 中的视图并不会生效。
- 因此,对于已经在 window 里的视图,可以采用从视图里移除并再次添加回去的方法使得 UIAppearance 的设置生效。
- 没法让已生成的实例立即生效:
- (void)btn_Tap:(UIButton *)gc_btn {
[[UIButton appearance] setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
}
- (void)btn_Tap:(UIButton *)gc_btn {
[[UIButton appearance] setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[gc_btn removeFromSuperview];
[self.view addSubview:gc_btn];
}
- 一个实现 UIAppearance 协议的类,都会有一个 _UIApperance 实例,保存着这个类通过 appearance 设置属性的 invocations,在该类被添加或应用到视图树上的时候,它会检查并调用这些属性设置。
- 这样就实现了让所有该类的实例都自动统一属性。appearance 只是起到一个代理作用,在特定的时机,让代理替所有实例做同样的事。