您的位置:首页 > 移动开发 > IOS开发

iOS图文混排

2015-11-16 20:52 537 查看
在很多新闻类或有文字展示的应用中现在都会出现图文混排的界面例如网易新闻等,乍一看去相似一个网页,其实这样效果并非由UIWebView 加载网页实现。现在分享一种比较简单的实现方式



iOS sdk中为我们提供了一套完善的文字排版开发组件:CoreText。CoreText库中提供了很多的工具来对文本进行操作,例如CTFont、CTLine、CTFrame等。利用这些工具可以对文字字体每一行每一段落进行操作。

此例中默认图片都在右上方,且为了美观和开发简便设定所占宽度都相同。

1. 首先,需要引入CoreText库

在需要使用的类文件中添加#import <CoreText/CoreText.h>头文件。

2. 设置文本的参数

创建一个NSMutableAttributedString对象,包含所需展示的文本字符串。这样就可以对其进行设置了。通过CTFontCreateWithName函数创建一个CTFont对象,利用NSMutableAttributedString对象的addAttribute方法进行设置。类似的方法可以设置字间距。

对其方式与行间距的设置方式:

[cpp] view
plaincopyprint?

// 文本对齐方式

CTTextAlignment alignment = kCTLeftTextAlignment;

CTParagraphStyleSetting alignmentStyle;

alignmentStyle.spec = kCTParagraphStyleSpecifierAlignment;

alignmentStyle.valueSize = sizeof(alignment);

alignmentStyle.value = &alignment;

// 创建设置数组

CTParagraphStyleSetting settings[] ={alignmentStyle};

CTParagraphStyleRef style = CTParagraphStyleCreate(settings, 1);

同样使用addAttribute设置字符串对象。这样的方法还可以设置行间距,段间距等参数。

3. 计算图片所占高度。图片可以使用UIImageView 来进行显示。很容易便可获取每张图片所占总高度。

4. 由于图片宽度是固定的这样就可以计算每行文字缩短的字数。只要文本的总体高度低于图像总高度则文字长度都是缩短的。用CTTypesetterSuggestLineBreak函数动态的计算每一行里的字数,因为每一行里面的中文字、标点符号、数字、字母都不一样所以可以显示的字数肯定也是不同的,所以需要作这样的计算。这样循环直至文本结束,就可以知道有多少行字了。再根据字体高度和行间距得出总的文本高度,如果文本高度大于图片总高度那么显示区域的Frame高度就是文本的高度,反之亦然。

5. 绘制文本:

设置每一行绘制文本的区间:

[cpp] view
plaincopyprint?

CFRange lineRange = CFRangeMake(currentIndex, lineLength);

建立文本行对象

CTLineRef line = CTTypesetterCreateLine(typeSetter, lineRange);

CGFloat x = [self textOffsetForLine:line inRect:self.bounds];

// 设置一行的位置

CGContextSetTextPosition(context, x, y);

// 绘制一行文字

CTLineDraw(line, context);

6. 其他功能:

在完成文本绘制功能后可以加入调整文字大小的功能,和图片的放大的功能。

文字大小可以通过直接设置字体大小后重新绘制文本来实现。

图片放大可以在视图上添加一个新的UIImageView 来展示放大后的图片,并且加入动画效

实现代码:

[cpp] view
plaincopy

void RunDelegateDeallocCallback( void* refCon ){

}

CGFloat RunDelegateGetAscentCallback( void *refCon ){

NSString *imageName = (NSString *)refCon;

return 80;//[UIImage imageNamed:imageName].size.height;

}

CGFloat RunDelegateGetDescentCallback(void *refCon){

return 0;

}

CGFloat RunDelegateGetWidthCallback(void *refCon){

NSString *imageName = (NSString *)refCon;

return 100;//[UIImage imageNamed:imageName].size.width;

}

先设置一个CTRun的委托,主要是用于指定对象的上行高,宽,或上下文释放时使用。

[cpp] view
plaincopy

-(void)drawCharAndPicture

{

CGContextRef context = UIGraphicsGetCurrentContext();

CGContextSetTextMatrix(context, CGAffineTransformIdentity);//设置字形变换矩阵为CGAffineTransformIdentity,也就是说每一个字形都不做图形变换

CGAffineTransform flipVertical = CGAffineTransformMake(1,0,0,-1,0,self.bounds.size.height);

CGContextConcatCTM(context, flipVertical);//将当前context的坐标系进行flip

NSLog(@"bh=%f",self.bounds.size.height);

NSMutableAttributedString *attributedString = [[[NSMutableAttributedString alloc] initWithString:@"请在这里插入一张图片位置"] autorelease];

//为图片设置CTRunDelegate,delegate决定留给图片的空间大小

NSString *imgName = @"img.png";

CTRunDelegateCallbacks imageCallbacks;

imageCallbacks.version = kCTRunDelegateVersion1;

imageCallbacks.dealloc = RunDelegateDeallocCallback;

imageCallbacks.getAscent = RunDelegateGetAscentCallback;

imageCallbacks.getDescent = RunDelegateGetDescentCallback;

imageCallbacks.getWidth = RunDelegateGetWidthCallback;

CTRunDelegateRef runDelegate = CTRunDelegateCreate(&imageCallbacks, imgName);

NSMutableAttributedString *imageAttributedString = [[NSMutableAttributedString alloc] initWithString:@" "];//空格用于给图片留位置

[imageAttributedString addAttribute:(NSString *)kCTRunDelegateAttributeName value:(id)runDelegate range:NSMakeRange(0, 1)];

CFRelease(runDelegate);

[imageAttributedString addAttribute:@"imageName" value:imgName range:NSMakeRange(0, 1)];

[attributedString insertAttributedString:imageAttributedString atIndex:4];

[cpp] view
plaincopy

//换行模式

CTParagraphStyleSetting lineBreakMode;

CTLineBreakMode lineBreak = kCTLineBreakByCharWrapping;

lineBreakMode.spec = kCTParagraphStyleSpecifierLineBreakMode;

lineBreakMode.value = &lineBreak;

lineBreakMode.valueSize = sizeof(CTLineBreakMode);

CTParagraphStyleSetting settings[] = {

lineBreakMode

};

CTParagraphStyleRef style = CTParagraphStyleCreate(settings, 1);

// build attributes

NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:(id)style forKey:(id)kCTParagraphStyleAttributeName ];

// set attributes to attributed string

[attributedString addAttributes:attributes range:NSMakeRange(0, [attributedString length])];

CTFramesetterRef ctFramesetter = CTFramesetterCreateWithAttributedString((CFMutableAttributedStringRef)attributedString);

CGMutablePathRef path = CGPathCreateMutable();

CGRect bounds = CGRectMake(0.0, 0.0, self.bounds.size.width, self.bounds.size.height);

CGPathAddRect(path, NULL, bounds);

CTFrameRef ctFrame = CTFramesetterCreateFrame(ctFramesetter,CFRangeMake(0, 0), path, NULL);

CTFrameDraw(ctFrame, context);

CFArrayRef lines = CTFrameGetLines(ctFrame);

CGPoint lineOrigins[CFArrayGetCount(lines)];

CTFrameGetLineOrigins(ctFrame, CFRangeMake(0, 0), lineOrigins);

NSLog(@"line count = %ld",CFArrayGetCount(lines));

for (int i = 0; i < CFArrayGetCount(lines); i++) {

CTLineRef line = CFArrayGetValueAtIndex(lines, i);

CGFloat lineAscent;

CGFloat lineDescent;

CGFloat lineLeading;

CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, &lineLeading);

NSLog(@"ascent = %f,descent = %f,leading = %f",lineAscent,lineDescent,lineLeading);

CFArrayRef runs = CTLineGetGlyphRuns(line);

NSLog(@"run count = %ld",CFArrayGetCount(runs));

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);

NSLog(@"width = %f",runRect.size.width);

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 = image.size;

imageDrawRect.origin.x = runRect.origin.x + lineOrigin.x;

imageDrawRect.origin.y = lineOrigin.y;

CGContextDrawImage(context, imageDrawRect, image.CGImage);

}

}

}

}

CFRelease(ctFrame);

CFRelease(path);

CFRelease(ctFramesetter);

}

效果:



从上面看大家可能没有发现什么问题,当把图片放在字的最左边会是什么样子的?



因此为了避免这种情况发生,我在代码中添加了换行模式。添加换行后的效果:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: