这里我封装了一个View用于显示文字与图片的混排,首先我们需要先在你的项目中添加coreText框架,在使用时直接导入头文件即可
核心代码:
.h中
#import <UIKit/UIKit.h>
#import <CoreText/CoreText.h>
使用代理,进行传值,这里我将段落的高度作为传递的参数,传给View的上级页面
@protocol ZHPCoreTextDelegate <NSObject>
@optional
- (void)ViewHeight:(CGFloat)height;
@end
@interface ZHPCoreTextView : UIView
- (id)initWithFrame:(CGRect)frame text:(NSString *)text imageNameArray:(NSArray *)imageNameArray delegate:(id<ZHPCoreTextDelegate>)delegate;
@property (nonatomic,copy)NSArray *imageArray;
@property (nonatomic,copy)NSString *text;
@property (nonatomic,assign)CGRect frame;
@property (nonatomic,strong)id<ZHPCoreTextDelegate> delegate;
@end
.m中
这里设置图片的高度与宽度
void RunDelegateDeallocCallback( void* refCon ){
}
CGFloat RunDelegateGetAscentCallback( void *refCon ){
NSString *imageName = (__bridge NSString *)refCon;
// return [UIImage imageNamed:imageName].size.height;
return 15;
}
CGFloat RunDelegateGetDescentCallback(void *refCon){
return 0;
}
CGFloat RunDelegateGetWidthCallback(void *refCon){
NSString *imageName = (__bridge NSString *)refCon;
// return [UIImage imageNamed:imageName].size.width;
return 25;
}
- (id)initWithFrame:(CGRect)frame text:(NSString *)text imageNameArray:(NSArray *)imageNameArray delegate:(id<ZHPCoreTextDelegate>)delegate
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor whiteColor];
_delegate = delegate;
_text = text;
_imageArray = imageNameArray;
}
return self;
}
drawRect,将图片与文字绘制在画布上
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@",_text] ];
//为所有文本设置字体
//[attributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:24] range:NSMakeRange(0, [attributedString length])]; // 6.0+
[attString addAttribute:(NSString *)kCTForegroundColorAttributeName value:(id)[UIColor orangeColor].CGColor range:NSMakeRange(0, _text.length)];
for (int i = _imageArray.count-1; i>= 0; i--) {
NSString *imageName = [_imageArray objectAtIndex:i];
CTRunDelegateCallbacks imageCallbacks;
imageCallbacks.version = kCTRunDelegateVersion1;
imageCallbacks.dealloc = RunDelegateDeallocCallback;
imageCallbacks.getAscent = RunDelegateGetAscentCallback;
imageCallbacks.getDescent = RunDelegateGetDescentCallback;
imageCallbacks.getWidth = RunDelegateGetWidthCallback;
CTRunDelegateRef runDelegate = CTRunDelegateCreate(&imageCallbacks, (__bridge void * _Nullable)(imageName));
NSMutableAttributedString *imageAttributedString = [[NSMutableAttributedString alloc] initWithString:@" "];//空格用于给图片留位置
[imageAttributedString addAttribute:(NSString *)kCTRunDelegateAttributeName value:(__bridge id)runDelegate range:NSMakeRange(0, 1)];
CFRelease(runDelegate);
// 图片所占字节大小
[imageAttributedString addAttribute:@"imageName" value:imageName range:NSMakeRange(0, 1)];
// 图片插入的位置
[attString insertAttributedString:imageAttributedString atIndex:_text.length];
}
UIFont *font = [UIFont systemFontOfSize:13];
CTFontRef fontRef = CTFontCreateWithName((__bridge CFStringRef)font.fontName,
font.pointSize,
NULL);
[attString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)fontRef range:NSMakeRange(0, [attString length])];
CFRelease(fontRef);
//换行模式
CTParagraphStyleSetting lineBreakMode;
CTLineBreakMode lineBreak = kCTLineBreakByCharWrapping;
lineBreakMode.spec = kCTParagraphStyleSpecifierLineBreakMode;
lineBreakMode.value = &lineBreak;
lineBreakMode.valueSize = sizeof(CTLineBreakMode);
//行距
CGFloat _linespace = 2.0f;
CTParagraphStyleSetting lineSpaceSetting;
lineSpaceSetting.spec = kCTParagraphStyleSpecifierLineSpacing;
lineSpaceSetting.value = &_linespace;
lineSpaceSetting.valueSize = sizeof(float);
//首行缩进
CGFloat fristlineindent = 24.0f;
CTParagraphStyleSetting fristline;
fristline.spec = kCTParagraphStyleSpecifierFirstLineHeadIndent;
fristline.value = &fristlineindent;
fristline.valueSize = sizeof(float);
CTParagraphStyleSetting settings[] = {
lineBreakMode,
lineSpaceSetting,
fristline
};
CTParagraphStyleRef style = CTParagraphStyleCreate(settings, 3);
// build attributes
NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:(id)style forKey:(id)kCTParagraphStyleAttributeName ];
// set attributes to attributed string
[attString addAttributes:attributes range:NSMakeRange(0, [attString length])];
CGMutablePathRef path = CGPathCreateMutable();
CGRect bounds = CGRectMake(0.0, 0.0, self.bounds.size.width, self.bounds.size.height);
CGPathAddRect(path, NULL, bounds);
CTFramesetterRef framesetter =
CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attString); //3
CTFrameRef frame =
CTFramesetterCreateFrame(framesetter,
CFRangeMake(0, 0), path, NULL);
CTFrameDraw(frame, context); //4
// 计算段落高度
NSArray *linesArray = (NSArray *) CTFrameGetLines(frame);
CGPoint origins[[linesArray count]];
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);
int line_y = (int) origins[[linesArray count] -1].y; //最后一行line的原点y坐标
CGFloat ascent;
CGFloat descent;
CGFloat leading;
CTLineRef line = (__bridge CTLineRef) [linesArray objectAtIndex:[linesArray count]-1];
CTLineGetTypographicBounds(line, &ascent, &descent, &leading);
[_delegate ViewHeight:self.bounds.size.height - line_y + (int) descent +1];
//+1为了纠正descent转换成int小数点后舍去的值
CFArrayRef lines = CTFrameGetLines(frame);
CGPoint lineOrigins[CFArrayGetCount(lines)];
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), lineOrigins);
for (int i = 0; i < CFArrayGetCount(lines); i++) {
CTLineRef line = CFArrayGetValueAtIndex(lines, i);
CGFloat lineAscent;
CGFloat lineDescent;
CGFloat lineLeading;
CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);
CFArrayRef runs = CTLineGetGlyphRuns(line);
for (int j = 0; j < CFArrayGetCount(runs); j++) {
CGFloat runAscent;
CGFloat runDescent;
CGPoint lineOrigin = lineOrigins[i];
CTRunRef run = CFArrayGetValueAtIndex(runs, j);
NSDictionary* attributes = (NSDictionary*)CTRunGetAttributes(run);
CGRect runRect;
runRect.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0,0), &runAscent, &runDescent, NULL);
runRect=CGRectMake(lineOrigin.x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL), lineOrigin.y - runDescent, runRect.size.width, runAscent + runDescent);
NSString *imageName = [attributes objectForKey:@"imageName"];
//图片渲染逻辑
if (imageName) {
UIImage *image = [UIImage imageNamed:imageName];
if (image) {
CGRect imageDrawRect;
imageDrawRect.size = CGSizeMake(20, 20);
imageDrawRect.origin.x = runRect.origin.x + lineOrigin.x ;
imageDrawRect.origin.y = lineOrigin.y-5;
CGContextDrawImage(context, imageDrawRect, image.CGImage);
}
}
}
}
CFRelease(frame); //5
CFRelease(path);
CFRelease(framesetter);
}
其他文件使用时:
- (void)viewDidLoad {
[super viewDidLoad];
_view = [[ZHPCoreTextView alloc] initWithFrame:CGRectMake(100, 100, self.view.frame.size.width/2, self.view.frame.size.height/2) text:@"什么时候变得不安,和没有归属感。然后还一味任性地埋怨,这不是我的错,如此焦躁,和孩子气的自己。忍不住的想放逐和流浪。看着依旧苍白的自己,像蒲草一样飘来飘去" imageNameArray:@[@"保.png",@"搬.png",@"票.png"] delegate:self];
_view.backgroundColor = [UIColor whiteColor];
_view.center = self.view.center;
[self.view addSubview:_view];
}
//通过代理方法获得段落的高度
- (void)ViewHeight:(CGFloat)height{
NSLog(@"段落高度为:%f",height);
}