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的聲明里面即可,它實際就是系統字體

 

如有錯誤歡迎更正


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM