内容简介:在 UIAppearance 出现之前,开发者如果想统一修改 app 内某一个控件的 UI 样式时,只能通过去修改每个控件的实例属性,对于只有几个实例的 UI 控件来说,这样的修改还可以接受,但如果整个 app 中有几十个,甚至上百个实例的时候,这样的修改就显得相当笨拙了,当然你也可以考虑使用一些黑魔法来实现,不过这或多或少都给开发者带来了不少麻烦。除了上面提到的场景外,还有一种场景就是在 app 内提供多种多样的主题来满足用户的需求,例如手淘在 app 内提供的主题切换功能。上面这两个实际开发中的应用场
在 UIAppearance 出现之前,开发者如果想统一修改 app 内某一个控件的 UI 样式时,只能通过去修改每个控件的实例属性,对于只有几个实例的 UI 控件来说,这样的修改还可以接受,但如果整个 app 中有几十个,甚至上百个实例的时候,这样的修改就显得相当笨拙了,当然你也可以考虑使用一些黑魔法来实现,不过这或多或少都给开发者带来了不少麻烦。
除了上面提到的场景外,还有一种场景就是在 app 内提供多种多样的主题来满足用户的需求,例如手淘在 app 内提供的主题切换功能。
上面这两个实际开发中的应用场景都映射出这样一个问题:如何在整个 app 中 高效,统一,即时
的定制 UI 控件样式。
在 iOS 5.0 之后,Apple 为开发者提供了名为 UIAppearance 和 UIAppearanceContainer 的类,它们就是苹果为开发者提供的一种官方解决方案。
为了在现有的 UIKit 框架里面去做这个事,苹果的思路是: 让 UIAppearance 成为一个可以返回代理的协议,通过它可以把任何配置转发给特定类的实例。
但为什么不直接在 UIView 里面搞个属性或者方法来做这件事儿呢?
因为诸如 UIBarButtonItem 这样的控件并不是 UIView 的子类,它只是持有一个视图实例而已。通过这种设计,UIAppearance 可以处理所有类型的 UI 控件,无论它是 UIView 的子类,还是包含了视图实例的非 UIView 控件。
阅读 UIAppearance 和 UIAppearanceContainer 的头文件
我们刚才说过 UIApearance 实际上是一个协议,该协议需实现以下几个方法:
+ (instancetype)appearance; + (instancetype)appearanceWhenContainedInInstancesOfClasses:(NSArray<Class <UIAppearanceContainer>> *)containerTypes NS_AVAILABLE_IOS(9_0); + (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait NS_AVAILABLE_IOS(8_0); + (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedInInstancesOfClasses:(NSArray<Class <UIAppearanceContainer>> *)containerTypes NS_AVAILABLE_IOS(9_0); // 已经废弃的两个方法 + (instancetype)appearanceWhenContainedIn:(nullable Class <UIAppearanceContainer>)ContainerClass, ... NS_REQUIRES_NIL_TERMINATION NS_DEPRECATED_IOS(5_0, 9_0, "Use +appearanceWhenContainedInInstancesOfClasses: instead") __TVOS_PROHIBITED; + (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedIn:(nullable Class <UIAppearanceContainer>)ContainerClass, ... NS_REQUIRES_NIL_TERMINATION NS_DEPRECATED_IOS(8_0, 9_0, "Use +appearanceForTraitCollection:whenContainedInInstancesOfClasses: instead") __TVOS_PROHIBITED; 复制代码
在这个协议中,需要简单解释一下第 3 和第 4 个方法,这个两个方法是用于解决 Size Classes 的问题而诞生的,通过这两个 API,我们可以控制在不同屏幕尺寸下的样式。
另外一个与之对应的协议是 UIAppearanceContainer,该协议并没有任何约定方法。因为它只是作为一个容器。
常见的,如 UIView 实现了 UIAppearance 这两种协议,既可以获取外观代理,也可以作为外观容器。而 UIViewController 则是仅实现了 UIAppearanceContainer 协议,很简单,它本身是控制器而不是 view,作为容器,为 UIView 等服务。
UIAppearance 的使用
哪些属性可以被 UIAppearance 调用?
在 UIKit 中被 UI_APPEARANCE_SELECTOR
宏标注的属性可以被 UIAppearance 调用。 例如 UIBarButtonItem 里的 tintColor 属性。
@property(nullable, nonatomic,strong) UIColor *tintColor NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; 复制代码
在 swift 中没有宏的概念,所以属性无法被 UI_APPEARANCE_SELECTOR
标注,如果想让某个属性支持 UIAppearance 可以为该属性使用 dynamic 关键字
何使用 UIAppearance 修改 UI 控件的属性?
UIAppearance 不仅可以修改某一类型控件的全部实例,也可以修改部分实例,开发者只需要使用正确的 API 即可。还是以 UIBarButtonItem 为例。
当我们想改变所有 UIBarButtonItem 实例的 tintColor 时,代码如下:
[[UIBarButtonItem appearance] setTintColor:myColor]; 复制代码
当我们想在某些指定容器类里改变 UIBarButtonItem 的 tintColor 时,我们可以这么做:
[[UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class]]] setTintColor:myColor]; 复制代码
UIAppearance 里与 UITraitCollection 相关的两个方法与上面两个方法的使用规则相似,在这里就不做赘述了
如何让 UIAppearance 生效?
使用 UIAppearance 只有在视图添加到 window 时才会生效,对于已经在 window 中的视图并不会生效。所以,对于已经在 window 里的视图,可以采用从视图里移除并再次添加回去的方法使得 UIAppearance 的设置生效。
所以如果你发现自己的设置没有生效的话,不妨参考下这个规则。
如何让自定义 view 的属性支持 UIAppearance
在实际使用过程中,我们绝大多数的视图类都继承自 UIView,UIView 的容器也基本上是 UIView 或 UIController,所以基本不需要开发者去实现这两个协议。
对于需要支持使用 UIAppearance 来设置的属性,在属性后增加 UI_APPEARANCE_SELECTOR
宏声明即可。
需要说明的是,在遵循 UIAppearanceContainer 协议的类中,声明与 UIAppearance 相关属性的方法时,要遵循两个代码规范:
UI_APPEARANCE_SELECTOR 标注
//You may have no axes or as many as you like for any property. - (void)setProperty:(PropertyType)property forAxis1:(IntegerType)axis1 axis2:(IntegerType)axis2 axisN:(IntegerType)axisN; - (PropertyType)propertyForAxis1:(IntegerType)axis1 axis2:(IntegerType)axis2 axisN:(IntegerType)axisN; //Example - (void)setBackgroundImage:(nullable UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; - (nullable UIImage *)backgroundImageForState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; 复制代码
UIAppearance 的探讨
Apple 官方对于 UIAppearance 的介绍并不多,网上的文章也大多都停留在使用层面上,今天我们来深入讨论一下 UIAppearance 内部的秘密。
没有什么卵用的 UI_APPEARANCE_SELECTOR
查看 Apple 的官方文档后,你就会发现它其实什么也没干…..
#define UI_APPEARANCE_SELECTOR 复制代码
既然它什么都没干,那么对于没有被 UI_APPEARANCE_SELECTOR
标记的属性,我们是否也能用 UIAppearance 进行统一设置呢?
答案是可以的。
此时你内心充满了疑惑,
我们不妨将这个问题分成两个部分:
- 第一部分就是为什么会有这个宏定义,如果真的没有它会有什么问题么?
- 第二部分就是 UIAppearance 是如何实现统一修改控件样式的呢?
真没问题么?
事实证明, UIAppearance 确实可以对一些没有被 UI_APPEARANCE_SELECTOR
标记的属性进行设置,甚至某些方法也是支持的 UIAppearance,例如 UISegmentedControl 的 setWidth:forSegmentAtIndex
方法。
但这种做法应该被避免,原因很简单:这些没有被标记的属性不一定会在未来的 iOS 版本中适用。
在 Understanding UIAppearance 这篇文章里也给出了同样的观点。
如何实现的?
那么 UIAppearance 到底是如何实现的呢?国外的大神们已经对此有了研究结论,我直接给出链接,感兴趣的朋友可以阅读一下 UIAppearance and Custom Views。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- RecyclerView使用指南(一)—— 基本使用
- 如何使用Meteorjs使用URL参数
- 使用 defer 还是不使用 defer?
- 使用 Typescript 加强 Vuex 使用体验
- [译] 何时使用 Rust?何时使用 Go?
- UDP协议的正确使用场合(谨慎使用)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JavaScript开发王
张亚飞 / 2008-6 / 65.00元
《网站开发专家·JavaScript开发王》详细介绍使用JavaScript脚本开发应用程序,并融合在网页中实现动态网页,内容也包含DHTML和AJAX。这《网站开发专家·JavaScript开发王》有严格的逻辑性,不论是JavaScript语言、DHTML,还是AJAX,《网站开发专家·JavaScript开发王》首先帮助用户建立首要的、基础的正确认知,然后通过实例,说明如何利用JavaScri......一起来看看 《JavaScript开发王》 这本书的介绍吧!
CSS 压缩/解压工具
在线压缩/解压 CSS 代码
RGB CMYK 转换工具
RGB CMYK 互转工具