当我们封装第三方库的时候,我们通常会希望将Api设置的更灵活一些,以方便使用者进行定制,在这些自定义的需求里面,UI的定制又是一个比较大的需求。 苹果在设计UIKit的时候,也提供了一个Protocol用于定制,那就是UIAppearance。
先看下UIAppearance的使用,试想一个场景,新建一个App,这个App的基础色是蓝色,NavigationBar都是蓝色背景,白色文字,同时所有页面的背景色都是浅灰色,文字默认的Font是宋体。如何实现这个需求呢?
先说通常的做法,很多App会基于系统的组件封装一套UI组件,这当然没有问题,比如我们继承UILabel,设计一个TestLabel,这个label会自动实现默认的font。 但是这种方式针对上面的需求,其实是有些重了,那有没有更轻一些的方法呢? 答案就是使用UIAppearance。
我们只需要在初始化的地方,设置一次基础颜色和字体等,后续创建的所有组件都会使用这个配置,先看下代码:
[UILabel appearance].font = [UIFont systemFontOfSize:13]; //只需设置一次
UILabel *label = [UIlabel new]; //后续所有的label都是默认13的字体
这里的appearance就是苹果统一设计的一套protocol,苹果的大部分UIKit相关的配置都会实现这套protocol,这样如果是系统性的配置,则只需要配置一次,后续就不用重复设置了。
看下appearance的声明:
@protocol UIAppearance
/* To customize the appearance of all instances of a class, send the relevant appearance modification messages to the appearance proxy for the class. For example, to modify the bar tint color for all UINavigationBar instances:
[[UINavigationBar appearance] setBarTintColor:myColor];
Note for iOS7: On iOS7 the tintColor property has moved to UIView, and now has special inherited behavior described in UIView.h.
This inherited behavior can conflict with the appearance proxy, and therefore tintColor is now disallowed with the appearance proxy.
*/
+ (instancetype)appearance;
/* To customize the appearances for instances of a class contained within an instance of a container class, or instances in a hierarchy, use +appearanceWhenContainedInInstancesOfClasses: for the appropriate appearance proxy. For example:
[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[UISplitViewController class]]] setBarTintColor:myColor];
[[UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[[UITabBarController class], [UISplitViewController class]]] setBarTintColor:myTabbedNavBarColor];
In any given view hierarchy the outermost appearance proxy wins. Specificity (depth of the chain) is the tie-breaker.
In other words, the containment statement is treated as a partial ordering. Given a concrete ordering (actual subview hierarchy), we select the partial ordering that is the first unique match when reading the actual hierarchy from the window down.
*/
+ (instancetype)appearanceWhenContainedIn:(nullable Class )ContainerClass, ... NS_REQUIRES_NIL_TERMINATION API_DEPRECATED_WITH_REPLACEMENT("appearanceWhenContainedInInstancesOfClasses:", ios(5.0, 9.0)) API_UNAVAILABLE(tvos);
+ (instancetype)appearanceWhenContainedInInstancesOfClasses:(NSArray> *)containerTypes API_AVAILABLE(ios(9.0));
+ (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait API_AVAILABLE(ios(8.0));
+ (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedIn:(nullable Class )ContainerClass, ... NS_REQUIRES_NIL_TERMINATION API_DEPRECATED_WITH_REPLACEMENT("appearanceForTraitCollection:whenContainedInInstancesOfClasses:", ios(8.0, 9.0)) API_UNAVAILABLE(tvos);
+ (instancetype)appearanceForTraitCollection:(UITraitCollection *)trait whenContainedInInstancesOfClasses:(NSArray> *)containerTypes API_AVAILABLE(ios(9.0));
@end
除了基础的appearance,苹果还提供了一些高级配置,用来方便针对不同的场景来进行配置,比如appearanceWhenContainedInInstancesOfClasses,当处于指定类的container上时的配置。
如果我们开发三方库的时候,支持appearance,只需要2步:
比如我们设计一个view,这个view支持appearance来设置背景颜色
@interface TestView : UIView
@property (nonatomic, strong) UIColor* bgColor UI_APPEARANCE_SELECTOR;
@end
@implementation TestView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [TestView appearance].bgColor;
// self.backgroundColor = [TestView appearanceWhenContainedInInstancesOfClasses:@[NSClassFromString(@"TestContainerView")]].bgColor;
}
return self;
}
@end
这里要特别注意下UI_APPEARANCE_SELECTOR这个宏,它的定义是
#define UI_APPEARANCE_SELECTOR __attribute__((annotate("ui_appearance_selector")))
另外我发现,如果不增加这个宏,也是一样可以内部实现appearance的,但是这应该是苹果的兼容措施,最好还是实现这个宏。
除了UI的部分,其他的配置同样可以使用appearace来设置,比如label的text字段,所以这个protocol其实是使用范围更广。
用的比较多的三方库,比如MBProgressHUD都支持appearance
https://www.jianshu.com/p/1af16a30baf4