Runtime中的Method Swizzle

示例: 一鍵改字體

1. 工程的Info.plist文件中需添加:

Info.plist添加內(nèi)容.png

其中l(wèi)oveway.ttf為字體冊資源文件的名稱.

2. Category類
UILabel+FontChange.h

//
//  UILabel+FontChange.h
//  FontChangeProject
//
//  Created by Brian on 16/02/13.
//  Copyright ? 2016年 Mac. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface UILabel (FontChange)

@end

UILabel+FontChange.m

//
//  UILabel+FontChange.m
//  FontChangeProject
//
//  Created by Brian on 16/02/13.
//  Copyright ? 2016年 Mac. All rights reserved.
//

#import "UILabel+FontChange.h"
#import <objc/runtime.h>

#define CustomFontName @"FZLBJW--GB1-0" // 字體冊.ttf文件中字體的名稱

@implementation UILabel (FontChange)

+ (void)load {
    // 方法交換應(yīng)該保證在程序中只會被執(zhí)行一次
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        // 1. 獲取UILabel的生命周期方法的selector
        SEL originalSel = @selector(willMoveToSuperview:);
        
        // 2. 獲取自己實現(xiàn)的將要被交換的方法的selector
        SEL overrideSel = @selector(myWillMoveToSuperview:);
        
        // 3. 實現(xiàn)方法交換
        MethodSwizzle([self class], originalSel, overrideSel);
    });
}

void MethodSwizzle(Class cls, SEL originalName, SEL overrideName) {
    // 獲取類中的某個實例的方法(減號方法)
    Method originalMethod = class_getInstanceMethod(cls, originalName);
    Method overrideMethod = class_getInstanceMethod(cls, overrideName);
    
    /** 原理: 有兩種情況要考慮一下,
        第一種情況是:要復(fù)寫的方法(override)并沒有在目標類中實現(xiàn);
        第二種情況是:這個方法(override)已經(jīng)存在于目標類中了.
     這兩種情況要區(qū)別對待.
     
     對于第一種情況,應(yīng)當(dāng)先在目標類中增加一個新的實現(xiàn)方法(override),運行時函數(shù)class_addMethod()如果
     發(fā)現(xiàn)該方法已經(jīng)存在,會返回NO;
     
     對于第二種情況,因為class_getInstanceMethod會返回父類的實現(xiàn),如果直接替換,就會替換掉父類的實現(xiàn),而不是目標類中的實現(xiàn). 
     這時需要在一個合適的位置來調(diào)用MethodSwizzle()方法, 在+ (void)load方法中調(diào)用就可以直接完成交換.
    */
    
    
    // class_addMethod()方法會讓originalName方法指向新的實現(xiàn)overrideMethod
    BOOL result = class_addMethod(cls, originalName, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod));
    if (result) {
        // 如果添加成功, 表示originalName方法已指向新的實現(xiàn)overrideMethod, 然后再使用class_replaceMethod()將新的方法overrideName指向原先的實現(xiàn)originalMethod, 就完成了交換.
        class_replaceMethod(cls, overrideName, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        // 如果添加失敗,就是第二種情況,這時可以直接通過method_exchangeImplementations來完成交換.
        method_exchangeImplementations(originalMethod, overrideMethod);
    }
}

- (void)myWillMoveToSuperview:(UIView *)newSuperview {
    [self myWillMoveToSuperview:newSuperview];
    
    // 自己的處理代碼...
    
    if ([self isKindOfClass:NSClassFromString(@"UIButtonLabel")]) {
        return;
    }
    if (self) {
        if (self.tag == 10086) {
            self.font = [UIFont systemFontOfSize:self.font.pointSize];
        } else {
            if ([UIFont fontNamesForFamilyName:CustomFontName]) {
                self.font = [UIFont fontWithName:CustomFontName size:self.font.pointSize];
            }
        }
    }
}

@end

Tips: 獲取字體冊中的字體名稱

/**
 讀取ttf或otf字體冊中的字體名稱 (先在Info.plist中把字體文件名添加到Fonts provided by application一項中)

 @param path 字體冊文件的路徑 如:[[NSBundle mainBundle] pathForResource:@"font12" ofType:@"ttf"]
 @param size 字體大小 如:18
 @return 字體
 */
- (UIFont *)customFontWithPath:(NSString *)path size:(CGFloat)size {
    NSURL *fontUrl = [NSURL fileURLWithPath:path];
    CGDataProviderRef fontDataProvider = CGDataProviderCreateWithURL((__bridge CFURLRef)fontUrl);
    CGFontRef fontRef = CGFontCreateWithDataProvider(fontDataProvider);
    CGDataProviderRelease(fontDataProvider);
    CTFontManagerRegisterGraphicsFont(fontRef, NULL);
    NSString *fontName = CFBridgingRelease(CGFontCopyPostScriptName(fontRef));
    NSLog(@"fontName = %@", fontName);
    UIFont *font = [UIFont fontWithName:fontName size:size];
    CGFontRelease(fontRef);
    return font;
}

/**
 讀取ttc字體冊中的字體名稱 (先在Info.plist中把字體文件名添加到Fonts provided by application一項中)

 @param path 字體冊文件的路徑 如:[[NSBundle mainBundle] pathForResource:@"font12" ofType:@"ttc"]
 @param size 字體大小 如:18
 @return 字體數(shù)組
 */
- (NSArray *)customFontArrayWithPath:(NSString *)path size:(CGFloat)size {
    CFStringRef fontPath = CFStringCreateWithCString(NULL, [path UTF8String], kCFStringEncodingUTF8);
    CFURLRef fontUrl = CFURLCreateWithFileSystemPath(NULL, fontPath, kCFURLPOSIXPathStyle, 0);
    CFArrayRef fontArray = CTFontManagerCreateFontDescriptorsFromURL(fontUrl);
    CTFontManagerRegisterFontsForURL(fontUrl, kCTFontManagerScopeNone, NULL);
    
    NSMutableArray *customFontArray = [NSMutableArray array];
    for (CFIndex i = 0; i < CFArrayGetCount(fontArray); i++) {
        CTFontDescriptorRef descriptor = CFArrayGetValueAtIndex(fontArray, i);
        CTFontRef fontRef = CTFontCreateWithFontDescriptor(descriptor, size, NULL);
        NSString *fontName = CFBridgingRelease(CTFontCopyName(fontRef, kCTFontPostScriptNameKey));
        NSLog(@"fontName[%ld] = %@", i, fontName);
        UIFont *font = [UIFont fontWithName:fontName size:size];
        [customFontArray addObject:font];
    }
    return customFontArray; 
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容