上篇主要是runtime的理論基礎(chǔ)(地址請戳這里),但是具體到項(xiàng)目中的時(shí)候很少會(huì)想到使用runtime來解決問題,本文主要根據(jù)筆者在項(xiàng)目中的某些需求,將使用runtime的某些場景記錄下來。
1 修改UITextField中placeholder的顏色以及文字大小
項(xiàng)目需求:textFiled為鏤空顯示父視圖的背景色,placeholder的顏色為白色,切字體字號為22px
1.1 解決問題思路
查看UITextField的屬性發(fā)現(xiàn)并沒有相關(guān)設(shè)置的屬性,使用runtime遍歷UITextField的屬性遍歷,并通過次變量修改placeholder的顏色和字體大小.
unsigned int count;
Ivar *ivarList = class_copyIvarList([UITextField class], &count);
for (int i = 0 ; i < count; i++) {
Ivar ivar = ivarList[i];
NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
//將第一個(gè)字符截取掉,獲得屬性名
NSString *propertyName = [name substringFromIndex:1];
//前面為成員變量,后面為屬性名
NSLog(@"%@:%@",name,propertyName);
}
查看控制臺打印結(jié)果(可以通過command+F搜索):

1.2 解決問題
UITextField *textFiled = [[UITextField alloc]initWithFrame:CGRectMake(0, 200, self.view.frame.size.width, 40)];
textFiled.backgroundColor = [UIColor clearColor];
textFiled.layer.borderColor = [UIColor whiteColor].CGColor;
textFiled.layer.borderWidth = 1;
textFiled.layer.cornerRadius = 4;
textFiled.placeholder = @"我是重生的占位符";
[textFiled setValue:[UIColor whiteColor] forKeyPath:@"_placeholderLabel.textColor"];
[textFiled setValue:[UIFont systemFontOfSize:11] forKeyPath:@"_placeholderLabel.font"];
[self.view addSubview:textFiled];
1.3 效果展示

但是該屬性變量屬于蘋果的私有API,使用該方法上線有可能會(huì)被拒,慎用.
2 項(xiàng)目中navigationBar的backItem不顯示文字
需求:項(xiàng)目中的大部分navigationBar的backItem不需要文字,只需要返回箭頭.
2.1 在不需要backItem文字的視圖控制器被push之前,設(shè)置backItem
UIBarButtonItem *backItem = [[UIBarButtonItem alloc]init];
backItem.title = @"";
self.navigationItem.backBarButtonItem = backItem;
設(shè)置該方法需要注意兩點(diǎn):需要在Push之前這是backBarButtonItem,否則無效;必須初始化一個(gè)新的UIBarButtonItem對象,設(shè)置其title為空并賦值,直接設(shè)置backBarButtonItem.title無效
頁面多的話,可以設(shè)置BaseController在其中設(shè)置,新生成的Controller繼承即可.
2.2 使用runtime解決
但是總覺的方法一這個(gè)不是解決問題的完美方法,想找到一個(gè)完美實(shí)現(xiàn)該需求且一勞永逸的方法。
使用category為UINavigationController增加擴(kuò)展方法
//在程序啟動(dòng)時(shí),使用runtime交換系統(tǒng)與自定義的方法
+(void)load{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method originalMethod = class_getInstanceMethod(self, @selector(backBarButtonItem));
Method customMethod = class_getInstanceMethod(self, @selector(CM_BackBarButtonItem));
method_exchangeImplementations(originalMethod, customMethod);
});
}
static char CMCustomBackBarButtonKey;
-(UIBarButtonItem *)CM_BackBarButtonItem{
UIBarButtonItem *item = [self CM_BackBarButtonItem];
if (item) {
return item;
}
item = objc_getAssociatedObject(self, &CMCustomBackBarButtonKey);
if (!item) {
item = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:NULL];
objc_setAssociatedObject(self, &CMCustomBackBarButtonKey, item, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return item;
}
2.3 效果展示

3 字體適配
項(xiàng)目中字體適配是很常見的情況,主要適配6p機(jī)型,常見的適配方法是在設(shè)置字體大小的時(shí)候乘以一個(gè)宏定義比例,該方法需要在每次設(shè)置字體的時(shí)候都要乘以比例,比較繁瑣.
runtime可以通過自定義的方法替換系統(tǒng)方法,因此又想到了runtime
3.1 解決思路:
需要設(shè)置字體大小的控件為UILabel和UIButton,通過分類分別為兩個(gè)類擴(kuò)展方法,自定義方法在子視圖加載之前重新設(shè)置控件的字體大小(通過判斷是否為6p乘以一個(gè)自定義的比例)
3.2 解決方法:
@implementation UIButton (Common)
+ (void)load {
//方法交換應(yīng)該被保證,在程序中只會(huì)執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL systemSel = @selector(willMoveToSuperview:);
SEL swizzSel = @selector(CM_WillMoveToSuperview:);
Method systemMethod = class_getInstanceMethod([self class], systemSel);
Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
//首先動(dòng)態(tài)添加方法,實(shí)現(xiàn)是被交換的方法,返回值表示添加成功還是失敗
BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
if (isAdd) {
//如果成功,說明類中不存在這個(gè)方法的實(shí)現(xiàn)
//將被交換方法的實(shí)現(xiàn)替換到這個(gè)并不存在的實(shí)現(xiàn)
class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
} else {
//否則,交換兩個(gè)方法的實(shí)現(xiàn)
method_exchangeImplementations(systemMethod, swizzMethod);
}
});
}
- (void)CM_WillMoveToSuperview:(UIView *)newSuperview {
[self CM_WillMoveToSuperview:newSuperview];
// if ([self isKindOfClass:NSClassFromString(@"UIButtonLabel")]) {
// return;
// }
if (self) {
//不需要適配的,通過設(shè)置tag值跳過
if (self.tag != 10086) {
self.titleLabel.font = [UIFont systemFontOfSize:self.titleLabel.font.pointSize*SizeScale];
} else {
}
}
}
@end
UILable同樣可以使用替換系統(tǒng)的方法實(shí)現(xiàn)字體的適配.
效果就不展示,具體大家可以自己運(yùn)行在模擬器查看。
3.3 設(shè)計(jì)優(yōu)化
將UILabel的分類和UIButton的分類合并寫在一個(gè)類里面。

.m文件
/**
button的字體適配
*/
@implementation UIButton (Common)
+ (void)load {
//方法交換應(yīng)該被保證,在程序中只會(huì)執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL systemSel = @selector(willMoveToSuperview:);
SEL swizzSel = @selector(CM_WillMoveToSuperview:);
Method systemMethod = class_getInstanceMethod([self class], systemSel);
Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
//首先動(dòng)態(tài)添加方法,實(shí)現(xiàn)是被交換的方法,返回值表示添加成功還是失敗
BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
if (isAdd) {
//如果成功,說明類中不存在這個(gè)方法的實(shí)現(xiàn)
//將被交換方法的實(shí)現(xiàn)替換到這個(gè)并不存在的實(shí)現(xiàn)
class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
} else {
//否則,交換兩個(gè)方法的實(shí)現(xiàn)
method_exchangeImplementations(systemMethod, swizzMethod);
}
});
}
- (void)CM_WillMoveToSuperview:(UIView *)newSuperview {
[self CM_WillMoveToSuperview:newSuperview];
// if ([self isKindOfClass:NSClassFromString(@"UIButtonLabel")]) {
// return;
// }
if (self) {
if (self.tag != 10086) {
self.titleLabel.font = [UIFont systemFontOfSize:self.titleLabel.font.pointSize*SizeScale];
} else {
}
}
}
@end
#pragma mark --- 字體設(shè)置
@implementation UILabel (Common)
+ (void)load {
//方法交換應(yīng)該被保證,在程序中只會(huì)執(zhí)行一次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL systemSel = @selector(willMoveToSuperview:);
SEL swizzSel = @selector(CM_WillMoveToSuperview:);
Method systemMethod = class_getInstanceMethod([self class], systemSel);
Method swizzMethod = class_getInstanceMethod([self class], swizzSel);
//首先動(dòng)態(tài)添加方法,實(shí)現(xiàn)是被交換的方法,返回值表示添加成功還是失敗
BOOL isAdd = class_addMethod(self, systemSel, method_getImplementation(swizzMethod), method_getTypeEncoding(swizzMethod));
if (isAdd) {
//如果成功,說明類中不存在這個(gè)方法的實(shí)現(xiàn)
//將被交換方法的實(shí)現(xiàn)替換到這個(gè)并不存在的實(shí)現(xiàn)
class_replaceMethod(self, swizzSel, method_getImplementation(systemMethod), method_getTypeEncoding(systemMethod));
} else {
//否則,交換兩個(gè)方法的實(shí)現(xiàn)
method_exchangeImplementations(systemMethod, swizzMethod);
}
});
}
- (void)CM_WillMoveToSuperview:(UIView *)newSuperview {
[self CM_WillMoveToSuperview:newSuperview];
// if ([self isKindOfClass:NSClassFromString(@"UIButtonLabel")]) {
// return;
// }
if (self) {
if (self.tag != 10086) {
self.font = [UIFont systemFontOfSize:self.font.pointSize*SizeScale];
} else {
}
}
}
@end