目录:
#import <Foundation/Foundation.h> //用于配置绘制的参数,例如文字颜色,大小,行间距 @interface CTFrameParserConfig : NSObject @property (nonatomic,assign)CGFloat width; @property (nonatomic,assign)CGFloat fontSize; @property (nonatomic,assign)CGFloat lineSpace; @property (nonatomic,strong)UIColor *textColor; @end #import "CTFrameParserConfig.h" @implementation CTFrameParserConfig -(id)init{ self = [super init]; if (self){ _width = 200.0f; _fontSize = 16.0f; _lineSpace = 0.0f; _textColor = RGB(150, 100, 100); } return self; } @end
#import <Foundation/Foundation.h> #import "CTFrameParserConfig.h" #import "CoreTextData.h" //用于最后绘制界面需要的CTFrameRef实例 @interface CTFrameParser : NSObject +(CoreTextData *)parseContext:(NSString *)context config:(CTFrameParserConfig *)config; +(NSDictionary *)attributesWithConfig:(CTFrameParserConfig *)config; @end #import "CTFrameParser.h" @implementation CTFrameParser +(NSDictionary *)attributesWithConfig:(CTFrameParserConfig *)config{ CGFloat fontSize = config.fontSize; CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL); CGFloat lineSpacing = config.lineSpace; const CFIndex kNumberOfSettings = 3; CTParagraphStyleSetting theSettings[kNumberOfSettings] = {{kCTParagraphStyleSpecifierLineSpacingAdjustment,sizeof(CGFloat),&lineSpacing}, {kCTParagraphStyleSpecifierMaximumLineSpacing,sizeof(CGFloat),&lineSpacing}, {kCTParagraphStyleSpecifierMinimumLineSpacing,sizeof(CGFloat),&lineSpacing}}; CTParagraphStyleRef theParagrapRef = CTParagraphStyleCreate(theSettings, kNumberOfSettings); UIColor *textColor = config.textColor; NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[(id)kCTForegroundColorAttributeName] = (id)textColor.CGColor; dict[(id)kCTFontAttributeName] = (__bridge id)fontRef; dict[(id)kCTParagraphStyleAttributeName] = (__bridge id)theParagrapRef; CFRelease(theParagrapRef); CFRelease(fontRef); return dict; } +(CoreTextData *)parseContext:(NSString *)context config:(CTFrameParserConfig *)config{ NSDictionary *attributes = [self attributesWithConfig:config]; NSAttributedString *contextString = [[NSAttributedString alloc]initWithString:context attributes:attributes]; CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)contextString); //获得要绘制区域的高度 CGSize restrictSize = CGSizeMake(config.width, CGFLOAT_MAX); CGSize coreTextSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), nil, restrictSize, nil); CGFloat textHeight = coreTextSize.height; CTFrameRef frame = [self createFrameWithFramesetter:framesetter config:config height:textHeight]; //将生成好的CTFrameRef实例和计算好的的绘制高度保存到CoreTectData实例中,最后返回CoreTectData实例 CoreTextData *data = [[CoreTextData alloc]init]; data.ctFrame =frame; data.height = textHeight; CFRelease(frame); CFRelease(framesetter); return data; } +(CTFrameRef)createFrameWithFramesetter:(CTFramesetterRef)framesetter config:(CTFrameParserConfig *)config height:(CGFloat)height{ //创建绘制的区域 CGMutablePathRef path = CGPathCreateMutable(); // CGPathAddRect(path, NULL, CGRectMake(0, 0, config.width, height)); CGPathAddRect(path, NULL, CGRectMake(0, -250, config.width+100, 200));//相反于UIKit,底层坐标 CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); CFRelease(path); return frame; } @end
#import <Foundation/Foundation.h> //保存CTFrameParser类生成的CTFrameRef,以及CTFrameRef实质绘制需要的高度 @interface CoreTextData : NSObject @property (nonatomic ,assign)CTFrameRef ctFrame; @property (nonatomic ,assign)CGFloat height;//实际使用场景汇总,需要先知道索要显示内容的高度之后才可以进行绘制 @end #import "CoreTextData.h" @implementation CoreTextData -(void)setCtFrame:(CTFrameRef)ctFrame{ if (_ctFrame != ctFrame){ if (_ctFrame !=nil){ CFRelease(_ctFrame); } CFRetain(ctFrame); _ctFrame = ctFrame; } } -(void)dealloc{ if (_ctFrame != nil){ CFRelease(_ctFrame); _ctFrame = nil; } } @end
#import <Foundation/Foundation.h> #import "CoreTextData.h" //持有CoreTextData的实例,负责将CTFrameRef绘制到界面上 @interface CTSubDisplayView : UIView @property (strong,nonatomic) CoreTextData *data; @end #import "CTSubDisplayView.h" @implementation CTSubDisplayView -(void)drawRect:(CGRect)rect{ [super drawRect:rect]; //得到当前绘制画布的上下文,用于后续将内容绘制在画布上 CGContextRef context = UIGraphicsGetCurrentContext(); //将坐标系上线翻转。对于底层的绘制引擎来说,屏幕的左下角是(0,0)坐标。而对于在上层的UIKit来说,左上角是(0,0)坐标。代码注释后效果如图1. CGContextSetTextMatrix(context, CGAffineTransformIdentity); CGContextTranslateCTM(context, 100, 100);// 设置UIKit x,y CGContextScaleCTM(context, 1.0, -1.0);//拉宽,拉高 if (self.data){ CTFrameDraw(self.data.ctFrame, context); } } @end
// PrefixHeader.pch #ifndef PrefixHeader_pch #define PrefixHeader_pch // Include any system framework and library headers here that should be included in all compilation units. // You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. /* iOS other 中 pch文件 将building setting中prefix header 路径设置$(SRCROOT)/$(PROJECT_NAME)/PrefixHeader.pch */ //NSLog and UIColor #import <UIKit/UIKit.h> #import <CoreText/CoreText.h> #import "CTFrameParserConfig.h" #import "UIView+frameAdjust.h" #ifdef DEBUG #define debuglog(...) NSLog(__VA_ARGS__) #define debugMethod() NSLog(@"%s",__func__) #else #define debugLog(...) #define debugMethod() #endif #define RGB(A,B,C) [UIColor colorWithRed:A/255.0 green:B/255.0 blue:C/255.0 alpha:1.0] #endif /* PrefixHeader_pch */
#import <UIKit/UIKit.h> @interface UIView (frameAdjust) -(CGFloat)x; -(void)setX:(CGFloat)x; -(CGFloat)y; -(void)setY:(CGFloat)y; -(CGFloat)height; -(void)setHeight:(CGFloat)height; -(CGFloat)width; -(void)setWidth:(CGFloat)width; @end #import "UIView+frameAdjust.h" @implementation UIView (frameAdjust) -(CGFloat)x{ return self.frame.origin.x; } -(void)setX:(CGFloat)x{ self.frame = CGRectMake(x, self.y, self.width, self.height); } -(CGFloat)y{ return self.frame.origin.y; } -(void)setY:(CGFloat)y{ self.frame = CGRectMake(self.x, y, self.width, self.height); } -(CGFloat)height{ return self.frame.size.height; } -(void)setHeight:(CGFloat)height{ self.frame = CGRectMake(self.x, self.y, self.width, height); } -(CGFloat)width{ return self.frame.size.width; } -(void)setWidth:(CGFloat)width{ self.frame = CGRectMake(self.x, self.y, width, self.height); } @end
#import <UIKit/UIKit.h> #import "CTSubDisplayView.h" #import "CTFrameParserConfig.h" @interface ViewController : UIViewController @property (nonatomic ,strong) CTSubDisplayView *ctView; @end #import "ViewController.h" #import "CTFrameParser.h" #import "CoreTextData.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; CTFrameParserConfig *config = [[CTFrameParserConfig alloc]init]; config.textColor = [UIColor redColor]; config.width = self.ctView.width; //定制排版文件格式 NSString *content = @"让CTFrameParser 支持接收NSAttributedString作为参数"; NSDictionary *attr = [CTFrameParser attributesWithConfig:config]; NSMutableAttributedString *attributesString = [[NSMutableAttributedString alloc]initWithString:content attributes:attr]; [attributesString addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(0, 7)]; // CoreTextData *data = [CTFrameParser parseContext:@"按照以上原则,我们将‘CTSubDisplayView’中的部分内容拆分" config:config]; CoreTextData *data = [CTFrameParser parseContext:content config:config];//颜色未生效 self.ctView.data = data; self.ctView.height = data.height; self.ctView.backgroundColor = [UIColor yellowColor]; self.view = self.ctView; } -(CTSubDisplayView *)ctView{ if (!_ctView){ _ctView = [[CTSubDisplayView alloc]init]; } return _ctView; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } @end