昨天在做界面开发的时候,碰到一个修改 UISearchbBar
上的cancle
取消按钮颜色的问题,跳转到UISearchBar
的头文件看API说明,并未找到可以修改其属性和方法。
然后我使用Xcode上的 Debug View Hierarchy 工具来查看图层,发现原来显示取消按钮的是一个叫 UINavigationButton
类的对象:
UINavigationButtion
感觉既熟悉又陌生,我在代码里尝试调用它,发现是个私有类,无法被获取。于是我想到了 Runtime ,用class_copyIvarList
来获取它所有的成员变量,通过遍历找到想要的对象名称,代码如下:
unsigned int memberCount = 0;
Ivar *ivarList = class_copyIvarList([UISearchBar class], &memberCount);
for (unsigned int j=0; j<memberCount; j++) {
Ivar ivar = ivarList[j];
const char *name = ivar_getName(ivar);
const char *type = ivar_getTypeEncoding(ivar);
NSLog(@"-[name]:%s [type]:%s",name,type);
}
free(ivarList);
通过日志找到 cancle
相关的名称:
找到了对应对象的名称,就可以通过KVC的 valueForKey
来获取对象的实例并修改它的属性,代码如下:
UIButton *cancleNavBtn = [self.searchBar valueForKey:@"_cancelButton"];
[cancleNavBtn setTitle:@"取消" forState:UIControlStateNormal];
[cancleNavBtn setTitleColor:[UIColor orangeColor] forState:UIControlStateNormal];
通过断点我们可以发现其实这个 button 就是之前的UINavigationButton
。
上面解决的方式还不够完美,我们再看日志下一行有个cancel相关的 UIBarButtonItem
,我推测UIBarButtonItem
应该是UINavigationButtion
的子类。
既然是 UIBarButtonItem
就应该可以用appearance
来修改全局的属性,于是我推导出正确修改取消按钮的方法:
[[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil]
setTitle:@"取消"];
NSDictionary *textDic = @{NSForegroundColorAttributeName:[UIColor blackColor]};
[[UIBarButtonItem appearanceWhenContainedIn:[UISearchBar class], nil]
setTitleTextAttributes:textDic
forState:UIControlStateNormal];
-
上面应该才是官方推荐的正确姿势,但这些内容并不是本篇文章的重点,我们回到刚才的
UINavigationButton
,这个私有button让我联想到几年前读的@我就叫Sunny怎么了的一篇文章从NSArray看类簇,内容是关于类簇(Class Clusters),那么 UIButton 算不算是一种类簇呢? -
类簇(Class Clusters)是什么?
类簇(Class Clusters) 是抽象工厂模式在iOS下的一种实现,在我们完全不知情的情况下,偷偷隐藏了很多具体的实现类,只暴露出简单的接口。
用通俗一点讲就是一个public
的抽象类加上一些private
的私有类构成的,它是对一些实现细节进行隐藏,而对外公开的行为进行统一的一种设计。
例如我们常用的NSNumber
,NSArray
,NSDictionary
以及NSString
等都属于类簇。
为了确定UIButton算不算是类簇,我们可以通过LLDB命令来验证这一点。
首先对UIButton的构造函数buttonWithType:
进行断点得到其运行地址,命令如下:
breakpoint set -F '+[UIButton buttonWithType:]'
再用地址来打印对应的汇编代码,命令如下:
dis -a 0x0000000xxxxxxxxx//这里的内存地址是随机的,根据上面获取的地址
然后我们会得到一大段汇编代码:注意一下每一行最后有Button关键字的地方,会发现一些平时没见过的私有Button,如UIPopoverButton
, UINavigationButton
, UITexturedButton
, _UIPlacardButton
,_UIShortPlacardButton
以及UIRoundedRectButton
等。
通过这些发现的private的私有类,那么我们就可以证明其实这里的UIButton是一个类簇。
总结:
刨根问底一直都是我们程序员追求的目标,本文通过一次简单的调试,意外发现UIButton也是类簇的一员,并使用强大的LLDB让我们了解到一些UIButton内部构造。遇到问题时,知其原理,才能事半功倍。