Objective-C對象接收到消息后,究竟會調(diào)用何種方法需要在運行時才能解析出來动分。對于給定的消息触幼,與之對應(yīng)的方法也可以在對象接收到這條消息的時候被替換成另外的方法。這一特性讓我們能夠在不需要源代碼恨闪,也不需要繼承子類來覆寫方法就能改變這個類本身的功能倘感,新功能可以在這個類的所有實例中生效。這個方案被稱為“方法調(diào)配”(Method Swizzling)咙咽。
在最近的項目中老玛,我們發(fā)現(xiàn)在不同屏幕上如果字體大小相同的話會不協(xié)調(diào),需要在小屏幕的手機上讓字體小一點钧敞,在大屏幕上的字體大一點蜡豹。這包括了UIlabel,UIButton,UITextField
以及UITextView。這些UIKit框架中的類使用非常頻繁溉苛,在每一個UIViewController中都會用到镜廉。要想實現(xiàn)“在不同屏幕上字體大小不同”這一需求的話,有兩種方案:一是自定義子類繼承對應(yīng)的UIKit類愚战,在自定義的子類中處理字體大小娇唯。還有一種方案就是使用runtime的黑魔法:Method Swizzling。替換掉原先的初始化方法寂玲,使用新的自定義的初始化方法塔插,在自定義的初始化方法中設(shè)置字體的大小。
下面以UILabel舉例:
#define ScreenScale [UIScreen mainScreen].bounds.size.width/375.f
@interface UILabel (FontSize)
@end
@implementation UILabel (FontSize)
+ (void)load{
Method imp = class_getInstanceMethod([self class], @selector(initWithCoder:));
Method myImp = class_getInstanceMethod([self class], @selector(myInitWithCoder:));
method_exchangeImplementations(imp, myImp);
Method cmp = class_getInstanceMethod([self class], @selector(initWithFrame:));
Method myCmp = class_getInstanceMethod([self class], @selector(myInitWithFrame:));
method_exchangeImplementations(cmp, myCmp);
}
- (id)myInitWithCoder:(NSCoder*)aDecode {
[self myInitWithCoder:aDecode];
if (self) {
CGFloat fontSize = self.font.pointSize;
CGFloat scale = [UIView getFontScale];
self.font = [self.font fontWithSize:fontSize* ScreenScale];
}
return self;
}
下面是demo在iPhone SE和iPhone 7 Plus上運行的效果拓哟∠胄恚可以看到,字體大小顯得自然了。使用Method Swizzling將再也不用再設(shè)置每一個控件的字體大小了流纹。
這樣糜烹,所有的UILabel的字體大小都會根據(jù)屏幕的尺寸進(jìn)行調(diào)整。
聲明:本文中用到的demo借鑒了開源的代碼FontSizeModify Created by dyw.感謝Effective Objective-C 2.0提供的思路疮蹦。