首页 > 其他分享 >UI基础 - UIAppearance

UI基础 - UIAppearance

时间:2022-10-12 01:33:07浏览次数:56  
标签:UIAppearance self 基础 视图 UIColor UI void DemoView UIView

前言

1 - 在一些 app 中会涉及到更改外观设置的功能,最普遍的就是夜间模式和白天模式的切换,而对于外观的更改必定是一个全局的东西。这在 iOS5 以前想要实现这样的效果是比较困难的,但是 iOS5 时 Apple 推出了 UIAppearance,使得外观的自定义更加容易实现

2 - 通常某个 app 都有自己的主题外观,而在自定义导航栏的时候或许是使用到如下面的代码

[UINavigationBar appearance].barTintColor = [UIColor  redColor];

appearance 是一个全局的效果,实际上它的作用就是统一外观设置

3 - 是否是所有的控件或者属性都可以这样设置?实际上能使用 appearance 的地方是在方法或者属性后面都带有一个 UI_APPEARANCE_SELECTOR 的宏,如下

1 // 属性
2 @property(nonatomic,assign) UIBarStyle barStyle UI_APPEARANCE_SELECTOR
3 // 方法
4 - (void)setTitleTextAttributes:(nullable NSDictionary<NSString *,id> *)attributes forState:(UIControlState)state NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR;

如何使用

1 - 如果我们自定义的视图也想要一个全局的外观设置,那么使用 UIAppearancel 来实现非常方便

2 - 代码示例

① 分别在 ViewController 和 SecondViewController 中的 touchesBegan 方法中初始化已经配置好的视图 DemoView

// - DemoView.h

#import <UIKit/UIKit.h>
@interface DemoView : UIView

// 两个视图:高同父视图,宽各占父视图的一半
@property (nonatomic, strong)UIView *leftView;
@property (nonatomic, strong)UIView *rightView;

// 修改视图外观(背景颜色)
// 修改两个子视图颜色:添加 UI_APPEARANCE_SELECTOR 宏
@property (nonatomic, strong)UIColor *leftColor  UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)UIColor *rightColor UI_APPEARANCE_SELECTOR;

@end

// - DemoView.m

 1 #import "DemoView.h"
 2 @implementation DemoView
 3 
 4 - (instancetype)initWithFrame:(CGRect)frame{
 5     self = [super initWithFrame:frame];
 6     if (self) {
 7         
 8         // 创建视图
 9         self.leftView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, frame.size.width/2.0, frame.size.height)];
10         [self addSubview:self.leftView];
11         
12         self.rightView = [[UIView alloc] initWithFrame:CGRectMake(frame.size.width/2.0, 0,frame.size.width/2.0, frame.size.height)];
13         [self addSubview:self.rightView];
14     }
15     return self;
16 }
17 
18 // 在 setter 方法中配置两个子视图的颜色
19 - (void)setLeftColor:(UIColor *)leftColor {
20     _leftColor = leftColor;
21     self.leftView.backgroundColor = _leftColor;
22 }
23 
24 - (void)setRightColor:(UIColor *)rightColor {
25     _rightColor = rightColor;
26     self.rightView.backgroundColor = _rightColor;
27 }
28 
29 @end

// - ViewController.m

 1 #import "ViewController.h"
 2 #import "SecondViewController.h"
 3 #import "DemoView.h"
 4 @interface ViewController()
 5 
 6 @end
 7 
 8 @implementation ViewController
 9 
10 - (void)viewDidLoad {
11     [super viewDidLoad];
12     self.view.backgroundColor = [UIColor cyanColor];
13     
14     // 全局配置
15     // 修改某一类型控件的全部实例 + (instancetype)appearance;
16     [DemoView appearance].leftColor = [UIColor redColor];
17     [DemoView appearance].rightColor = [UIColor blueColor];
18 
19     // 下一页
20     [self createNextPageBtn];
21     
22 }
23 -(void)createNextPageBtn{
24     
25     UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
26     [btn setTitle:@"下一页" forState:UIControlStateNormal];
27     btn.backgroundColor = [UIColor darkGrayColor];
28     btn.frame = CGRectMake(40, 50, SCREEN_WIDTH-80, 50);
29     [btn addTarget:self action:@selector(nextPage) forControlEvents:UIControlEventTouchUpInside];
30     [self.view addSubview:btn];
31 }
32 
33 -(void)nextPage{
34     
35     SecondViewController *secVC = [[SecondViewController alloc] init];
36     secVC.view.backgroundColor = [UIColor brownColor];
37     [self.navigationController pushViewController:secVC animated:YES];
38     
39 }
40 
41 // 在 touchesBegan 创建视图,注意查看效果(全局配置)
42 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
43     
44     DemoView *cardView = [[DemoView alloc] initWithFrame:CGRectMake(40, 120, 200, 100)];
45     [self.view addSubview:cardView];
46 }
47 
48 @end

// - SecondViewController.m

 1 #import "SecondViewController.h"
 2 #import "DemoView.h"
 3 @implementation SecondViewController
 4 
 5 - (void)viewDidLoad {
 6     [super viewDidLoad];
 7     
 8 }
 9 // 在 touchesBegan 创建视图,注意查看效果(全局配置)
10 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
11     DemoView * cardView = [[DemoView alloc] initWithFrame:CGRectMake(40, 120, 200, 100)];
12     [self.view addSubview:cardView];
13 }
14 
15 @end

运行效果:两个视图控制器中 DemoView 的背景颜色均在全局配置实现

     

 

② 当然 UIAppearance 不仅可以修改某一类型控件的全部实例,也可以修改部分实例,开发者只需要使用正确的 API 即可,比如我们修改 ViewController 中的方法

 1 // 在 touchesBegan 创建视图,注意查看效果(全局配置)
 2 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
 3     
 4     DemoView *cardView = [[DemoView alloc] initWithFrame:CGRectMake(40, 120, 200, 100)];
 5     [self.view addSubview:cardView];
 6 
 7     // 添加的代码:修改某一控件的部分实例
 8     // 添加以下代码后:第一个界面的 DemoView背景颜色由原来的 左红右蓝 变成 左黄右蓝;而第二个界面保持原样
 9     // + (instancetype)appearanceWhenContainedInInstancesOfClasses:(NSArray<Class <UIAppearanceContainer>> *)containerTypes NS_AVAILABLE_IOS(9_0);
10     [DemoView appearanceWhenContainedInInstancesOfClasses:@[[self class]]].leftColor = [UIColor yellowColor];
11 }

剖析 UIAppearance

1 - 查看 API 发现 iOS5.0 之后提供的不仅是 UIAppearance,还有另外一个叫做 UIAppearanceContainer 的类(实际上他们都是 protocol )

 1 // UIAppearanceContainer 协议并没有任何约定方法,因为它只是作为一个容器
 2 // 比如 UIView 实现了 UIAppearance 中的协议,既可以获取外观代理,也可以作为外观容器
 3 // 而 UIViewController 则是仅实现了 UIAppearanceContainer 协议,它本身是控制器,并不是 view。但是它作为容器,就可以为 UIView 等服务
 4 
 5 // 事实 UIView 的容器也基本上是 UIView 或 UIViewController,基本不需要自己去实现这两个协议
 6 // 对于需要支持使用 appearance 来设置的属性,在属性后增加 UI_APPEARANCE_SELECTOR 宏声明即可
 7 @protocol UIAppearanceContainer <NSObject> @end
 8 
 9 
10 @protocol UIAppearance <NSObject>
11 // 返回接受外观设置的代理
12 + (instancetype)appearance;
13 
14 // 当出现在某个类的出现时候才会改变
15 + (instancetype)appearanceWhenContainedIn:(nullable Class <UIAppearanceContainer>)ContainerClass, ... NS_REQUIRES_NIL_TERMINATION API_DEPRECATED_WITH_REPLACEMENT("appearanceWhenContainedInInstancesOfClasses:", ios(5.0, 9.0)) API_UNAVAILABLE(tvos);
16 
17 // 针对不同 trait 下的应用的 apperance 进行简单设定
18 // 这两个 appearanceForTraitCollection方法是用于解决 Size Classes 的问题而诞生的,通过这两个 API 我们可以控制在不同屏幕尺寸下的样式
19 + (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait API_AVAILABLE(ios(8.0));
20 + (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedInInstancesOfClasses:(NSArray<Class <UIAppearanceContainer>> *)containerTypes  API_AVAILABLE(ios(9.0));
21 // 已经废弃的方法
22 + (instancetype)appearanceWhenContainedInInstancesOfClasses:(NSArray<Class <UIAppearanceContainer>> *)containerTypes API_AVAILABLE(ios(9.0));
23 + (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedIn:(nullable Class <UIAppearanceContainer>)ContainerClass, ... NS_REQUIRES_NIL_TERMINATION API_DEPRECATED_WITH_REPLACEMENT("appearanceForTraitCollection:whenContainedInInstancesOfClasses:", ios(8.0, 9.0)) API_UNAVAILABLE(tvos);
24 
25 @end

显然苹果的思路是让 UIAppearance 成为一个可以返回代理的协议,通过它可以把任何配置转发给特定类的实例。这样做的好处就是 UIAppearance 可以处理所有类型的UI控件:无论它是 UIView 的子类、还是包含了视图实例的非 UIView 控件

 

标签:UIAppearance,self,基础,视图,UIColor,UI,void,DemoView,UIView
From: https://www.cnblogs.com/self-epoch/p/16783162.html

相关文章

  • 2022 最新 Java 基础 面试题(二)
    2022最新Java基础面试题(二)​​下面列出这份Java面试问题列表包含的主题​​​​1、Java中能创建volatile数组吗?​​​​2、volatile能使得一个非原子操作变成原......
  • React基础入门
    1.React是什么?react是一个用于构建用户界面的JavaScript库2.React特点声明式UI(你只需要描述UI(HTML)看起来是什么样的,就跟写HTML一样)组件化组件是react中最重要......
  • 【机器学习】数据科学基础——机器学习基础实践(一)
    【机器学习】数据科学基础——机器学习基础实践(一)作者简介:在校大学生一枚,华为云享专家,阿里云星级博主,腾云先锋(TDP)成员,云曦智划项目总负责人,全国高等学校计算机教学与产业实......
  • DenseCLIP Language-Guided Dense Prediction with Context-Aware Prompting论文阅读
    摘要作者首先回顾CLIP,说道使用图像-文本对进行大规模预训练得到的模型可以很容易迁移到下游任务。然后指出目前还没有人做过将从图像-文本对学到的知识应用于密集预测任务......
  • Netty学习之NIO基础
    Netty学习之NIO基础本博客是根据黑马程序员Netty实战学习时所做的笔记可先参考博客JavaNIO一、三大组件简介Channel与BufferJavaNIO系统的核心在于:通道(Channel)和......
  • 防火墙基础之多分支无线网络综合部署与区域与区域之间安全防护
    防火墙基础之多分支无线网络综合部署与区域与区域之间安全防护​原理概述:​防火墙(英语:Firewall)技术是通过有机结合各类用于安全管理与筛选的软件和硬件设备,帮助计算机网络于......
  • python基础之闭包函数与装饰器
    python基础之闭包函数与装饰器目录一、global与nonlocal二、函数名的多种用法1.可以当变量名2.可以当函数的参数3.可以当函数的返回值三、闭包函数1.闭包函数的实际应用四......
  • springboot Druid后台监控功能和过滤
    @ControllerpublicclassDruidConfig{@ConfigurationProperties(prefix="spring.datasource")@BeanpublicDataSourcedruidDataSource(){return......
  • 16、Java——QuickHit游戏
    目录​​项目需求​​​​项目环境准备​​​​案例覆盖的技能点​​​​难点分析​​​​项目实现思路​​​​代码展示​​项目需求  (1)QuickHit游戏考验学员键盘输入......
  • 【code基础】stream流简化数组的求最大值
    将集合或者数组转化为流,进行求最大值,排序,可以省去for循环,简化代码量Arrays.stream(res).max().getAsInt()可以得到res数组的最大值Arrays.stream(res).sorted().boxed(......