UIKit: UIApearance

文章详细介绍了iOS5之后引入的UIAppearance功能,它允许开发者通过协议来获取类的外观代理,进而自定义控件外观。通过使用UIAppearance,开发者能够轻松修改应用程序中所有或特定容器内的控件外观,包括按钮、导航栏等。文章还提供了实例代码,展示了如何利用UIAppearance修改控件的背景颜色、字体大小和文本颜色等属性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文章开头先援引一下Mattt Thompson大神在UIApearance里的一句话吧:

1
Users will pay a premium for good-looking software.

就如同大多数人喜欢看帅哥美女一样,一款App能不能被接受,长得怎样很重要。虽然大家都明白“人不可貌相”这个理,但大多数人其实还是视觉动物。用户体验用户体验,如果都让用户看得不爽了,又何谈用户体验呢?所以…所以…哎,我也只能在这默默地码字了。

iOS 5以前,我们想去自定义系统控件的外观是一件麻烦的事。如果想统一地改变系统控件的外观,我们可能会想各种办法,如去继承现有的控件类,并在子类中修改,或者甚至于动用method swizzling这样高大上的方法。不过,苹果在iOS 5之后为我们提供了一种新的方法:UIAppearance,让这些事简单了不少。在这里,我们就来总结一下吧。

UIApearance是作用

UIApearance实际上是一个协议,我们可以用它来获取一个类的外观代理(appearance proxy)。为什么说是一个类,而不明确说是一个视图或控件呢?这是因为有些非视图对象(如UIBarButtonItem)也可以实现这个协议,来定义其所包含的视图对象的外观。我们可以给这个类的外观代理发送一个修改消息,来自定义一个类的实例的外观。

我们以系统定义的控件UIButton为例,根据我们的使用方式,可以通过UIAppearance修改整个应用程序中所有UIButton的外观,也可以修改某一特定容器类中所有UIButton的外观(如UIBarButtonItem)。不过需要注意的是,这种修改只会影响到那些执行UIAppearance操作之后添加到我们的视图层级架构中的视图或控件,而不会影响到修改之前就已经添加的对象。因此,如果要修改特定的视图,先确保该视图在使用UIAppearance后才通过addSubview添加到视图层级架构中。

UIAppearance的使用

如上面所说,有两种方式来自定义对象的外观:针对某一类型的所有实例;针对包含在某一容器类的实例中的某一类型的实例。讲得有点绕,我把文档的原文贴出来吧。

1
for all instances, and for instances contained within an instance of a container class.

为此,UIAppearance声明了两个方法。如果我们想自定义一个类所有实例的外观,则可以使用下面这个方法:

1
2
3
4
5
// swift
static func appearance() -> Self  // Objective-C + (instancetype)appearance 

例如,如果我们想修改UINavigationBar的所有实例的背影颜色和标题外观,则可以如下实现:

1
2
3
4
5
6 
UINavigationBar.appearance().barTintColor = UIColor(red: 104.0/255.0, green: 224.0/255.0, blue: 231.0/255.0, alpha: 1.0)  UINavigationBar.appearance().titleTextAttributes = [  NSFontAttributeName: UIFont.systemFontOfSize(15.0),  NSForegroundColorAttributeName: UIColor.whiteColor() ] 

我们也可以指定一类容器,在这个容器中,我们可以自定义一个类的所有实例的外观。我们可以使用下面这个方法:

1
+ (instancetype)appearanceWhenContainedIn:(Class<UIAppearanceContainer>)ContainerClass, ... 

如,我们想修改导航栏中所有的按钮的外面,则可以如下处理:

1
2
3
4
5
6 7 8 9 10 11 
[[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], nil]  setBackgroundImage:myNavBarButtonBackgroundImage forState:state barMetrics:metrics];  [[UIBarButtonItem appearanceWhenContainedIn:[UINavigationBar class], [UIPopoverController class], nil]  setBackgroundImage:myPopoverNavBarButtonBackgroundImage forState:state barMetrics:metrics];  [[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], nil]  setBackgroundImage:myToolbarButtonBackgroundImage forState:state barMetrics:metrics];  [[UIBarButtonItem appearanceWhenContainedIn:[UIToolbar class], [UIPopoverController class], nil]  setBackgroundImage:myPopoverToolbarButtonBackgroundImage forState:state barMetrics:metrics]; 

注意这个方法的参数是一个可变参数,因此,它可以同时设置多个容器。

我们仔细看文档,发现这个方法没有swift版本,至少我在iOS 8.xSDK中没有找到对应的方法。呵呵,如果想在iOS 8.x以下的系统用swift来调用appearanceWhenContainedIn,那就乖乖地用混编吧。

不过在iOS 9SDK中(记录一下,今天是2015.07.18),又把这个方法给加上了,不过这回参数换成了数组,如下所示:

1
2
@available(iOS 9.0, *) static func appearanceWhenContainedInInstancesOfClasses(containerTypes: [AnyObject.Type]) -> Self 

嗯,这里有个问题,我在Xcode 7.0 beta 3版本上测试swift版本的这个方法时,把将其放在启动方法里面,如下所示:

1
2
3
4
5
6 7 8 9 10 11 12 13 14 
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {   // 此处会崩溃,提示EXC_BAD_ACCESS  let barButtonItemAppearance = UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses([UINavigationBar.self])   let attributes = [  NSFontAttributeName: UIFont.systemFontOfSize(13.0),  NSForegroundColorAttributeName: UIColor.whiteColor()  ]   barButtonItemAppearance.setTitleTextAttributes(attributes, forState: .Normal)   return true } 

程序崩溃了,在appearanceWhenContainedInInstancesOfClasses这行提示EXC_BAD_ACCESS。既然是内存问题,那就找找吧。我做了如下几个测试:

1.拆分UIBarButtonItem.appearanceWhenContainedInInstancesOfClasses,在其前面加了如下几行代码:

1
2
3
4
5
let appearance = UIBarButtonItem.appearance()  let arr: [AnyObject.Type] = [UINavigationBar.self, UIToolbar.self]  print(arr) 

可以看到除了appearanceWhenContainedInInstancesOfClasses自身外,其它几个元素都是没问题的。

2.将这段拷贝到默认的ViewController中,运行。同样崩溃了。

3.在相同环境下(Xcode 7.0 beta 3 + iOS 9.0),用Objective-C对应的方法试了一下,如下:

1
2
3
4
5
6 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {   [UIBarButtonItem appearanceWhenContainedInInstancesOfClasses:@[[UINavigationBar class]]];   return YES; } 

程序很愉快地跑起来了。

额,我能把这个归结为版本不稳定的缘故么?等到稳定版出来后再研究一下吧。

支持UIAppearance的组件

iOS 5.0后,有很多iOSAPI都已经支持UIAppearance的代理方法了,Mattt ThompsonUIApearance中,给我们提供了以下两行脚本代码,可以获取所有支持UI_APPEARANCE_SELECTOR的方法(我们将在下面介绍UI_APPEARANCE_SELECTOR):

1
2
3
$ cd /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS*.sdk/System/Library/Frameworks/UIKit.framework/Headers  $ grep -H UI_APPEARANCE_SELECTOR ./* | sed 's/ __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_5_0) UI_APPEARANCE_SELECTOR;//' 

大家可以试一下,我这里列出部分输出:

1
2
3
4
5
6 7 8 9 
./UIActivityIndicatorView.h:@property (readwrite, nonatomic, retain) UIColor *color NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ./UIAppearance.h:/* To participate in the appearance proxy API, tag your appearance property selectors in your header with UI_APPEARANCE_SELECTOR. ./UIAppearance.h:#define UI_APPEARANCE_SELECTOR __attribute__((annotate("ui_appearance_selector"))) ./UIBarButtonItem.h:- (void)setBackgroundImage:(UIImage *)backgroundImage forState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ./UIBarButtonItem.h:- (UIImage *)backgroundImageForState:(UIControlState)state barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; ./UIBarButtonItem.h:- (void)setBackgroundImage:(UIImage *)backgroundImage forState:(UIControlState)state style:(UIBarButtonItemStyle)style barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; ./UIBarButtonItem.h:- (UIImage *)backgroundImageForState:(UIControlState)state style:(UIBarButtonItemStyle)style barMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(6_0) UI_APPEARANCE_SELECTOR; ./UIBarButtonItem.h:- (void)setBackgroundVerticalPositionAdjustment:(CGFloat)adjustment forBarMetrics:(UIBarMetrics)barMetrics NS_AVAILABLE_IOS(5_0) UI_APPEARANCE_SELECTOR; 

转载于:https://www.cnblogs.com/panyuluoye/p/4980554.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值