一个APP开发都尾声时候,老板一句说这字体好小,做一个切换字体的功能吧!例如QQ微信那样的。我当时就想这应该是用个全局宏定义,就可以吧!最后发现,我的工程项目是用storyboard来创建的,所以没有例如手写代码有一个宏定义的字体定义。全工程都由Xib创建,IB上修改和设置字体。瞬间懵逼了。
不过如果使用宏定义,我猜想也是不可以的,因为你修改了宏,页面的字体也不会因为宏的改变而去变化。修改了宏并不会去触发页面刷新,需要用全局定义宏,可能需要一个触发页面刷新的方法。也就需要用到通知或者别的。
runTime控制字体
我在网上百度了许久,也去翻墙使用google来搜索一些外文网页。只有看到一些类似的,主题更换资料。有一个博主有写了一个方法,使用runTime(运行时)来控制APP程序中的字体变化。写一个UILabel的分类,在分类用使用+ (void)load { }使用class_getInstanceMethod(Class cls, SEL name)方法获取自定义方法和系统方法,在使用method_exchangeImplementations(Method m1, Method m2)方法替换系统的方法。
以下是这方法的代码:
+ (void)load {
Method imp = class_getInstanceMethod([self class], @selector(initWithCoder:));
Method myImp = class_getInstanceMethod([self class], @selector(myInitWithCoder:));
method_exchangeImplementations(imp, myImp);
}
- (id)myInitWithCoder:(NSCoder*)aDecode {
[self myInitWithCoder:aDecode];
if (self) {
//使用UD获取本地保存的字体大小
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSNumber *num = [userDefault objectForKey:@"Font"];
CGFloat fontSize = self.font.pointSize;
self.font = [UIFont systemFontOfSize:fontSize + [num floatValue]];
}
return self;
}
只要写好这个分类,只需要丢入工程内就可以直接实现APP的字体变化,方便快捷。不过这方法有个弊端,就是修改字体后,需要重新启动APP,才能实现。本人对于APP的内存管理知识不是很全面,大概理解是当APP页面将要出现的时候都会运行+ (void)load { }方法,不过当页面已经出现内存已经存在时候就不会运行+ (void)load { }方法(也可以自己去百度下这方法)如果有大神懂这里面的原由,可以方便告知我下。
控制器基类-遍历
之后我使用基类UIViewController 让每个基于控制器页面都继承于这个类,就是所谓的基类。然后再这基类中接收一个通知KVO,触发方法来循环遍历页面中所有view.subviews
判断是否为UIButton和UILabel。以下为代码:
@interface BaseViewController ()
@end
@implementation BaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tongzhi:) name:@"tongzhi" object:nil];
}
- (void)tongzhi:(NSNotification *)text{
NSLog(@"%@",text.userInfo[@"font"]);
NSLog(@"-----接收到通知------");
NSNumber *num = text.userInfo[@"font"];
[self setFont:[num floatValue] forView:self.view];
}
- (void)setFont:(CGFloat)fontSize forView:(UIView*)view{
if ([view isKindOfClass:[UILabel class]]) {
UILabel *lbl = (UILabel *)view;
CGFloat font = lbl.font.pointSize;
lbl.font = [UIFont systemFontOfSize:font + fontSize];
}
if ([view isKindOfClass:[UIButton class]]) {
UIButton *btn = (UIButton *)view;
CGFloat font = btn.titleLabel.font.pointSize;
btn.titleLabel.font = [UIFont systemFontOfSize:font + fontSize];
}
for (UIView *sview in view.subviews) {
[self setFont:fontSize forView:sview];
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"tongzhi" object:nil];
}
此方法可以实现字体的改变,不过略微复杂,因为是每个子类都循环遍历,可能会有系统运行速度过慢等问题。还有因为我的是工程全部使用 storyboard 来搭建的页面,其中我也使用了UITableViewController,虽然UITableViewController也是继承于UIViewController,就可能需要使用多继承,Objective-C又不支持多继承,需要用协议来实现,感觉有些麻烦,也许是因为本人知识不够。所以没有继续下去
设计思路:控件基类-通知-基类获取通知
第三种,也是我现在使用的一种。这种方法就比前面说的简单,并且不会出现问题。这是求教了我以前的老大,也根据网上一些类似的功能,主题切换,来实现的。
首先,我创建一个继承于UILabel的基类,在这基类中添加通知KVO,监听字体。
核心代码:
@interface BaseLabel ()
@property (assign, nonatomic) CGFloat fontSize;
@end
@implementation BaseLabel
- (id)init {
self = [super init];
if (self != nil) {
NSLog(@"我出现了 init ");
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self != nil) {
NSLog(@"我出现了 initWithCoder");
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSNumber *num = [userDefault objectForKey:@"Font"];
CGFloat font = self.font.pointSize;
self.font = [UIFont systemFontOfSize:font + [num floatValue]];
self.fontSize = font;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fontNotification:) name:@"tongzhi" object:nil];
NSLog(@"我出现了 awakeFromNib %f",self.fontSize);
}
#pragma mark - NSNotification Actions
- (void)fontNotification:(NSNotification *)notification {
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
NSNumber *num = [userDefault objectForKey:@"Font"];
self.font = [UIFont systemFontOfSize:self.fontSize + [num floatValue]];
}
#pragma mark - Memery Manager
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:@"tongzhi" object:nil];
}
只需要将工程中的UILabel基于此类就可以,直接实现字体的变化。使用一些空间约束来调整因为字体变化的原因而照成的控件变化问题。(例如tableViewCell高度的变化,需要重新计算。可以直接使用Label的基类中的通知放到控制器上,来监听变化,实现cell高度的动态变化)
UIButton也可以使用此方法来控制字体的变化。
最后注意一点,我们修改APP字体的时候,我们需要保存在标准字号和修改后字体之间的差值,我设置了一个标准字号14,因为APP中有这不同的Label字体。
这是工程在GitHub上的地址:changeLabelFont