iOS开发-使用网络特殊字体下载CoreText
在开发中遇到需要将字体下载后再显示的情况,这种特殊字体下载后才能正常。
在AFNetworking中添加
pod 'Reachability'
字体下载器使用AFNetworking实现将字体文件下载
代码如下
#import "SDFontDownloaderClient.h"
#import "AFNetworking.h"
@implementation SDFontDownloaderClientError
@end
@interface SDFontDownloaderClient ()
@property (nonatomic, strong) AFHTTPSessionManager *httpManager;
@end
@implementation SDFontDownloaderClient
+ (instancetype)sharedInstance {
static SDFontDownloaderClient *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[SDFontDownloaderClient alloc] init];
_sharedInstance.httpManager = [AFHTTPSessionManager manager];
_sharedInstance.httpManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];
});
return _sharedInstance;
}
- (AFHTTPSessionManager *)httpManager{
if (!_httpManager) {
_httpManager = [[AFHTTPSessionManager alloc] init];
_httpManager.operationQueue.maxConcurrentOperationCount = 6;
_httpManager.requestSerializer = [AFJSONRequestSerializer serializer];
[_httpManager.requestSerializer willChangeValueForKey:@"timeoutInterval"];
[_httpManager.requestSerializer setTimeoutInterval:10];
[_httpManager.requestSerializer setStringEncoding:NSUTF8StringEncoding];
_httpManager.responseSerializer = [AFJSONResponseSerializer serializer];
_httpManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/plain", @"multipart/form-data", @"application/json", @"text/html", @"image/jpeg", @"image/png", @"application/octet-stream", @"text/json", @"text/javascript", @"text/html", nil];
_httpManager.requestSerializer.HTTPMethodsEncodingParametersInURI = [NSSet setWithArray:@[@"POST", @"GET", @"HEAD", @"PUT", @"DELETE"]];
}
[_httpManager.requestSerializer setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
return _httpManager;
}
#pragma mark - Http Request Failure
- (SDFontDownloaderClientError *)httpRequestFailure:(NSHTTPURLResponse *)response
error:(NSError *)error {
SDFontDownloaderClientError *e = [[SDFontDownloaderClientError alloc] init];
if(error.code == NSURLErrorNotConnectedToInternet || error.code == NSURLErrorCannotFindHost || error.code == NSURLErrorCannotConnectToHost){
e.message = @"网络连接失败!";
return e;
}
if (error.code == NSURLErrorTimedOut){
e.message = @"网路连接超时!";
return e;
}
NSInteger statusCode = response.statusCode;
if (statusCode == 401) {
e.message = @"认证失败";
} else if (statusCode == 400){
e.message = @"无效请求";
} else if (statusCode == 404) {
e.message = @"访问的资源丢失了!";
} else if (statusCode >= 500){
e.message = @"服务器居然累倒了!";
}
#ifdef DEBUG
@try {
// 这里只是测试用
//第一步、首先从error根据NSErrorFailingURLKey拿到value
NSError *errorFail = [error.userInfo objectForKey:@"NSUnderlyingError"];
//第二步、通过errorFail根据com.alamofire.serialization.response.error.data拿到value
NSData *data = nil;
if (errorFail) {
data = [errorFail.userInfo objectForKey:@"com.alamofire.serialization.response.error.data"];
} else {
data = [error.userInfo objectForKey:@"com.alamofire.serialization.response.error.data"];
}
NSLog(@"data:%@",data);
//第三部、将NSData转成NSString,因为NSString字符串比较直观
if (data) {
NSString *errorString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"errorString:%@",errorString);
}
} @catch (NSException *exception) {
} @finally {
}
#else
#endif
return e;
}
#pragma mark - Http download
/**
请求下载
@param aUrl aurl
@param aSavePath aSavePath
@param aFileName aFileName
@param aTag aTag
@param downloadprogress downloadprogress
@param success success
@param failure failure
*/
- (void)downloadFileURL:(NSString *)aUrl
savePath:(NSString *)aSavePath
fileName:(NSString *)aFileName
tag:(NSInteger)aTag
downloadProgress:(void(^)(CGFloat progress))downloadprogress
success:(void(^)(NSURLResponse *response,NSString *filePath))success
failure:(void(^)(SDFontDownloaderClientError * e))failure {
NSFileManager *fileManger = [NSFileManager defaultManager];
if ([fileManger fileExistsAtPath:[aSavePath stringByAppendingPathComponent:aFileName]]) {
//文件存在
return;
}
//2.确定请求的URL地址
NSString *requestUrl = aUrl;
NSMutableURLRequest *request = [self.httpManager.requestSerializer requestWithMethod:@"GET" URLString:requestUrl parameters:nil error:nil];
__block NSURLSessionDownloadTask *downloadTask = nil;
downloadTask = [self.httpManager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
NSLog(@"progress current thread:%@", [NSThread currentThread]);
dispatch_async(dispatch_get_main_queue(), ^{
downloadprogress(1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount);
});
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
NSLog(@"destination current thread:%@", [NSThread currentThread]);
return [NSURL fileURLWithPath:aSavePath];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
NSLog(@"completionHandler current thread:%@", [NSThread currentThread]);
if(error == nil) {
success(response,[filePath path]);
} else {
//下载失败
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
SDFontDownloaderClientError *e = [self httpRequestFailure:httpResponse error:error];
failure(e);
}
}];
[downloadTask resume];
}
@end
字体文件在下载后,将注册到CTFontManager
//注册指定路径下的字体文件
- (void)registerFont:(NSString *)fontPath {
//调整位置
NSURL *fontUrl = [NSURL fileURLWithPath:fontPath];
CGDataProviderRef providerRef = CGDataProviderCreateWithURL((__bridge CFURLRef)fontUrl);
CFErrorRef error;
CGFontRef font = CGFontCreateWithDataProvider(providerRef);
if(!font){
CGDataProviderRelease(providerRef);
CGFontRelease(font);
return;
}
BOOL ctfmrgf = CTFontManagerRegisterGraphicsFont(font, &error);
if (!ctfmrgf) {
//注册失败
CFStringRef errorDescription = CFErrorCopyDescription(error);
CFRelease(errorDescription);
if (error) {
CFRelease(error);
}
}
CGFontRelease(font);
CFRelease(providerRef);
}
字体管理完整代码如下
#import "SDFontManager.h"
@implementation SDFontLoadError
@end
static SDFontManager *manager = nil;
@interface SDFontManager ()
@end
@implementation SDFontManager
+ (instancetype)shareInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[SDFontManager alloc] init];
});
return manager;
}
- (void)downloadAppleFontName:(NSString *)fontName
fontSize:(CGFloat)fontSize
beginLoadBlock:(SDFontBeginLoadBlock)beginLoadBlock
progressBlock:(SDFontLoadProgressBlock)progressBlock
completionBlock:(SDFontLoadCompletionBlock)completionBlock {
self.beginLoadBlock = beginLoadBlock;
self.progressBlock = progressBlock;
self.completionBlock = completionBlock;
UIFont* aFont = [UIFont fontWithName:fontName size:fontSize];
// If the font is already downloaded
if (aFont && ([aFont.fontName compare:fontName] == NSOrderedSame || [aFont.familyName compare:fontName] == NSOrderedSame)) {
// Go ahead and display the sample text.
if (self.beginLoadBlock) {
self.beginLoadBlock();
}
if (self.progressBlock) {
self.progressBlock(1.0);
}
if (self.completionBlock) {
self.completionBlock(aFont, nil);
}
return;
}
// Create a dictionary with the font's PostScript name.
NSMutableDictionary *attrs = [NSMutableDictionary dictionaryWithObjectsAndKeys:fontName, kCTFontNameAttribute, nil];
// Create a new font descriptor reference from the attributes dictionary.
CTFontDescriptorRef desc = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)attrs);
NSMutableArray *descs = [NSMutableArray arrayWithCapacity:0];
[descs addObject:(__bridge id)desc];
CFRelease(desc);
__block BOOL errorDuringDownload = NO;
// Start processing the font descriptor..
// This function returns immediately, but can potentially take long time to process.
// The progress is notified via the callback block of CTFontDescriptorProgressHandler type.
// See CTFontDescriptor.h for the list of progress states and keys for progressParameter dictionary.
CTFontDescriptorMatchFontDescriptorsWithProgressHandler( (__bridge CFArrayRef)descs, NULL, ^(CTFontDescriptorMatchingState state, CFDictionaryRef progressParameter) {
//NSLog( @"state %d - %@", state, progressParameter);
double progressValue = [[(__bridge NSDictionary *)progressParameter objectForKey:(id)kCTFontDescriptorMatchingPercentage] doubleValue];
if (state == kCTFontDescriptorMatchingDidBegin) {
dispatch_async( dispatch_get_main_queue(), ^ {
// Show an activity indicator
// 开始下载字体,显示加载进度
if (self.beginLoadBlock) {
self.beginLoadBlock();
}
NSLog(@"Begin Matching");
});
} else if (state == kCTFontDescriptorMatchingDidFinish) {
dispatch_async( dispatch_get_main_queue(), ^ {
// Remove the activity indicator
if (!errorDuringDownload) {
NSLog(@"%@ downloaded", fontName);
}
if(self.progressBlock){
self.progressBlock(1.0f);
}
// 完成下载字体
if (self.completionBlock) {
if ([self isAvaliableFont:fontName fontSize:fontSize]) {
[self saveAppleFontPathWithFontName:fontName];
UIFont *aFont = [UIFont fontWithName:fontName size:fontSize];
self.completionBlock(aFont, nil);
} else {
NSLog(@"font %@ is Unavaliable", fontName);
}
}
});
} else if (state == kCTFontDescriptorMatchingWillBeginDownloading) {
dispatch_async( dispatch_get_main_queue(), ^ {
// Show a progress bar
if(self.progressBlock){
self.progressBlock(0.0f);
}
NSLog(@"Begin Downloading");
});
} else if (state == kCTFontDescriptorMatchingDidFinishDownloading) {
dispatch_async( dispatch_get_main_queue(), ^ {
// Remove the progress bar
if(self.progressBlock){
self.progressBlock(1.0f);
}
NSLog(@"Finish downloading");
});
} else if (state == kCTFontDescriptorMatchingDownloading) {
dispatch_async( dispatch_get_main_queue(), ^ {
// Use the progress bar to indicate the progress of the downloading
if(self.progressBlock){
self.progressBlock(progressValue / 100.0);
}
NSLog(@"Downloading %.0f%% complete", progressValue);
});
} else if (state == kCTFontDescriptorMatchingDidFailWithError) {
// An error has occurred.
// Get the error message
NSError *error = [(__bridge NSDictionary *)progressParameter objectForKey:(id)kCTFontDescriptorMatchingError];
NSString *errorMessage = nil;
if (error != nil) {
errorMessage = [error description];
} else {
errorMessage = @"ERROR MESSAGE IS NOT AVAILABLE!";
}
// Set our flag
errorDuringDownload = YES;
dispatch_async( dispatch_get_main_queue(), ^ {
if (self.completionBlock) {
SDFontLoadError *error = [[SDFontLoadError alloc] init];
error.errorMessage = errorMessage;
self.completionBlock(nil, error);
}
NSLog(@"Download error: %@", errorMessage);
});
}
return (bool)YES;
});
}
- (void)downloadCustomFontName:(NSString *)fontName
fontDownloadUrl:(NSString *)fontDownloadUrl
fontSize:(CGFloat)fontSize
beginLoadBlock:(SDFontBeginLoadBlock)beginLoadBlock
progressBlock:(SDFontLoadProgressBlock)progressBlock
completionBlock:(SDFontLoadCompletionBlock)completionBlock {
self.beginLoadBlock = beginLoadBlock;
self.progressBlock = progressBlock;
self.completionBlock = completionBlock;
UIFont* aFont = [UIFont fontWithName:fontName size:fontSize];
// If the font is already downloaded
if (aFont && ([aFont.fontName compare:fontName] == NSOrderedSame || [aFont.familyName compare:fontName] == NSOrderedSame)) {
// Go ahead and display the sample text.
if (self.beginLoadBlock) {
self.beginLoadBlock();
}
if (self.progressBlock) {
self.progressBlock(1.0);
}
if (self.completionBlock) {
self.completionBlock(aFont, nil);
}
return;
}
//如果不存在,重新下载解压
NSString *savefontDirectoryPath = [self fontDirectoryPath];
//下载成功
NSString *afileName = [[NSURL URLWithString:fontDownloadUrl] lastPathComponent];
NSString *afilePath = [NSString pathWithComponents:@[savefontDirectoryPath, afileName]];
//下载成功
NSString *aFontFileName = [[NSURL URLWithString:fontDownloadUrl] lastPathComponent];
BOOL exsit = [self exsitCustomFontFileWithFontName:aFontFileName];
if (exsit) { //如果已经下载过了
// 检查字体是否可用
[self registerFont:afilePath];
UIFont *font = [self fontWithPath:afilePath fontSize:fontSize];
//更新UI
if (self.progressBlock) {
self.progressBlock(1.0);
}
if (self.completionBlock) {
self.completionBlock(font, nil);
}
return;
}
__weak typeof(self) weakSelf = self;
[[SDFontDownloaderClient sharedInstance] downloadFileURL:fontDownloadUrl savePath:afilePath fileName:afilePath tag:[afilePath hash] downloadProgress:^(CGFloat progress) {
if (self.progressBlock) {
self.progressBlock(progress);
}
} success:^(NSURLResponse *response, NSString *filePath) {
NSLog(@"filePath:%@",filePath);
dispatch_async(dispatch_get_main_queue(), ^{
NSString *fontPath = filePath;
[weakSelf registerFont:fontPath]; //注册字体文件
UIFont *font = [weakSelf fontWithPath:fontPath fontSize:fontSize];
if (weakSelf.completionBlock) {
weakSelf.completionBlock(font, nil);
}
});
} failure:^(SDFontDownloaderClientError *e) {
dispatch_async(dispatch_get_main_queue(), ^{
SDFontLoadError *error = [[SDFontLoadError alloc] init];
error.errorMessage = e.message;
if (weakSelf.completionBlock) {
weakSelf.completionBlock(nil, error);
}
});
}];
}
//注册苹果字体并保存路径(这里苹果的字体不在沙盒目录,无法注册)
- (void)saveAppleFontPathWithFontName:(NSString *)fontName{
CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)fontName, 0., NULL);
CFURLRef fontURL = CTFontCopyAttribute(fontRef, kCTFontURLAttribute);
NSURL *fontPathURL = (__bridge NSURL*)(fontURL);
//把苹果的字体路径保存起来
[self registerFont:fontPathURL.path]; //注册字体
CFRelease(fontURL);
CFRelease(fontRef);
}
//注册指定路径下的字体文件
- (void)registerFont:(NSString *)fontPath {
//调整位置
NSURL *fontUrl = [NSURL fileURLWithPath:fontPath];
CGDataProviderRef providerRef = CGDataProviderCreateWithURL((__bridge CFURLRef)fontUrl);
CFErrorRef error;
CGFontRef font = CGFontCreateWithDataProvider(providerRef);
if(!font){
CGDataProviderRelease(providerRef);
CGFontRelease(font);
return;
}
BOOL ctfmrgf = CTFontManagerRegisterGraphicsFont(font, &error);
if (!ctfmrgf) {
//注册失败
CFStringRef errorDescription = CFErrorCopyDescription(error);
CFRelease(errorDescription);
if (error) {
CFRelease(error);
}
}
CGFontRelease(font);
CFRelease(providerRef);
}
- (UIFont *)fontWithPath:(NSString *)fontPath fontSize:(CGFloat)fontSize {
NSURL *fontUrl = [NSURL fileURLWithPath:fontPath];
CGDataProviderRef providerRef = CGDataProviderCreateWithURL((__bridge CFURLRef)fontUrl);
CGFontRef font = CGFontCreateWithDataProvider(providerRef);
if(!font){
CGDataProviderRelease(providerRef);
CGFontRelease(font);
return nil;
}
CGDataProviderRelease(providerRef);
CTFontManagerUnregisterGraphicsFont(font,nil);
CTFontManagerRegisterGraphicsFont(font, NULL);
NSString *newFamilyName = CFBridgingRelease(CGFontCopyPostScriptName(font));
UIFont *uifont = [UIFont fontWithDescriptor:[UIFontDescriptor fontDescriptorWithName:newFamilyName size:fontSize] size:fontSize];
CGFontRelease(font);
return uifont;
}
//判断字体是否可用
- (BOOL)isAvaliableFont:(NSString *)fontName fontSize:(CGFloat)fontSize {
UIFont *aFont = [UIFont fontWithName:fontName size:fontSize];
return aFont && ([aFont.fontName compare:fontName] == NSOrderedSame || [aFont.familyName compare:fontName] == NSOrderedSame);
}
//是否存在fontFileName
- (BOOL)exsitCustomFontFileWithFontName:(NSString *)fontName {
NSString *fontPath = [[self fontDirectoryPath] stringByAppendingPathComponent:fontName];
BOOL exsit = [[NSFileManager defaultManager] fileExistsAtPath:fontPath];
return exsit;
}
// 字体存储的路径path
- (NSString *)fontDirectoryPath {
NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *fontDirectoryPath = [documentsDirectory stringByAppendingPathComponent:@"fonts"];
[self createDirectoryIfNotExsitPath:fontDirectoryPath]; //创建目录
return fontDirectoryPath;
}
//创建目录
- (BOOL)createDirectoryIfNotExsitPath:(NSString *)path {
BOOL success = YES;
if(![[NSFileManager defaultManager] fileExistsAtPath:path]){ //如果则创建文件夹
NSError * error = nil;
success = [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:&error];
if (!success || error) {
NSLog(@"Error! %@", error);
} else {
NSLog(@"Create fonts directory Success!");
}
}
return success;
}
//依fontName构建font
- (UIFont *)fontWithFontName:(NSString *)fontName fontSize:(CGFloat)fontSize{
if ([self isAvaliableFont:fontName fontSize:fontSize]) {
return [UIFont fontWithName:fontName size:fontSize];
}
return nil;
}
@end
我这里使用下载注册后的字体
- (void)lookButtonAction {
[[SDFontManager shareInstance] downloadCustomFontName:@"猫啃网糖圆体" fontDownloadUrl:@"https://j1-common-bucket.s3.cn-northwest-1.amazonaws.com.cn/as/2020/12/16/oLmJQK1608104260841.ttf" fontSize:20 beginLoadBlock:^{
NSLog(@"beginLoadBlock");
} progressBlock:^(CGFloat progress) {
NSLog(@"progressBlock:%f", progress);
} completionBlock:^(UIFont *font, SDFontLoadError *error) {
NSLog(@"completionBlock font:%@, error:%@", font, error);
if (font && !error) {
self.titleLabel.font = font;
}
}];
}
#pragma mark - lazy
- (UILabel *)titleLabel {
if (!_titleLabel) {
_titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_titleLabel.backgroundColor = [UIColor clearColor];
_titleLabel.textColor = [UIColor blackColor];
_titleLabel.font = [UIFont systemFontOfSize:11];
_titleLabel.textAlignment = NSTextAlignmentCenter;
}
return _titleLabel;
}
iOS开发-使用网络特殊字体下载CGFontRef
在开发中遇到需要将字体下载后再显示的情况,这种特殊字体下载后才能正常。。
学习记录,每天不停进步。