Objective-C Runtime使用之全局字体替换为第三方字体(iOS)

 前言:

  iOS开发里头,常用的设置字体方式是使用UIFont的systemFontOfSize这个Class Method,在一半情况下都算够用。

最近有设计师朋友问能不能在客户端中使用特定的字体,答案是可以的,我们可以通过手动给工程添加配置字体的ttf文件(字体库)

然后通过fontWithName:name size:size这个 Class Method即可选用,然而在一个已经经过长时间开发的客户端,会有历史遗漏问题

导致整个工程的字体配置可能存在修改工作量大,改漏改错等情况,针对这种情况我们也可以通过runtime来解决。

 

1、导入第三方字体

首先需要下载一个.ttf为后缀的文件,也就是字体库。下载后将文件导入工程,如图

接着需要在工程配置info.plist中添加这个字体

在info.plist中添加一行,key是Fonts provided by application,中文意思即 字体由应用程序提供

这是个array对象,那么我们把它展开

往里面添加一个item,内容即我们刚刚添加的那个文件名

然后在Build Phases里添加资源文件 如图

 

接下来可以在工程中,通过UIFont 这个类 遍历我们现在可以用的字体集和字体名字

遍历代码如下

    NSArray *fontFamilys = [UIFont familyNames];
    for (NSString *familyName in fontFamilys) {
        NSLog(@"family name : %@",familyName);
        NSArray *fontNames = [UIFont fontNamesForFamilyName:familyName];
        for (NSString *fontName in fontNames) {
            NSLog(@"font name : %@",fontName);
        }
    }

注意 ,不同的iOS大版本之间,可使用的字体库会有差异,但是我们这里只需要取到我们手动添加的字体

 

 

 遍历出来的内容很多,不翻页也不好找到我们添加的字体。

我这里添加的字体是微软雅黑,那么我搜一下

也是可以找到的,这里我们需要取font name,即图上的2016-11-21 09:49:45.780 FontDemo[17853:921926] font name : MicrosoftYaHei

取到字体名字,我们就可以通过

[UIFont fontWithName:@"MicrosoftYaHei" size:16];

 

fontWithName: size: 这个类方法去得到我们需要的UIFont对象,也就是雅黑字体

 

------------------------------不华丽的分割线--------------------------

 

好了,单个字体的更换这里是实现了,但是我这里需要的是全局的字体修改

接下来的内容又要接触到objc runtime 的method exchange了,也就是method swizzling 

在Objective-c中,hook方案能解决很多问题,这里的问题是其中之一

但是这种全局设置的方法交换也有一定的局限性,比如 我需要再换其他字体呢? 这个问题后面再探讨

 

开始设置method swizzling

首先 建立一个UIFont的categroy

在.m文件中 实现load方法,并调用父类load

+ (void)load{
    [super load];
}

接着 做method swizzling的过程 只需要调用一次,

那么可以用gcd的once 执行,

+ (void)load{
    [super load];
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Method oldMethod = class_getClassMethod([self class], @selector(systemFontOfSize:));
        Method newMethod = class_getClassMethod([self class], @selector(__nickyfontchanger_YaheiFontOfSize:));
        method_exchangeImplementations(oldMethod, newMethod);
    });
}

别忘了#import <objc/runtime.h> 

解析一下上面这几句代码

首先Method即方法,class_getClassMethod这是获取类方法,因为我们原来使用的systemFontOfSize是个类方法。

如果要交换的是实例方法,那么就要用class_getInstanceMethod 获取

先获取旧的方法,再获取新的方法,新的方法是写在这个category里的

像我这里:

+ (UIFont *)__nickyfontchanger_YaheiFontOfSize:(CGFloat)fontSize{
    UIFont *font = [UIFont fontWithName:@"MicrosoftYaHei" size:fontSize];
    if (!font)return [self __nickyfontchanger_YaheiFontOfSize:fontSize];
    return font;
}

再来解析一下这个方法的执行:

首先获取我们的第三方字体,若字体不存在,则返回系统默认字体

但是为什么我返回系统默认字体的时候,调用的是 [self __nickyfontchanger_YaheiFontOfSize:fontSize]呢?

因为方法已经交换了,实际上这个方法的pointer指向的是系统的systemFontOfSize这个方法

具体的实现

 

 那么再运行一下工程看看?

 

ps:问题来了

我要单独给某个字体设置成系统字体怎么办?

事实上我们这里只是把两个方法交换了而已,所以我们只要把+ (UIFont *)__nickyfontchanger_YaheiFontOfSize:(CGFloat)fontSize;这个方法写到.h的声明里面即可,它实际就是系统字体

 

如有错误欢迎更正

posted @ 2016-11-21 10:29  老徐想减肥  阅读(3069)  评论(0编辑  收藏  举报