[转] iOS文字排版(CoreText)那些事儿
文章转载自 http://www.cocoachina.com/applenews/devnews/2014/0521/8504.html
iOS文字排版(CoreText)那些事儿


转自阿毛的蛋疼地
第一次比较深入接触iOS文字排版相关内容是在12年底,实现某IM项目聊天内容的图文混排,照着nimbus的AttributedLabel和Raywenderlish上的这篇文章《Core Text Tutorial for iOS: Making a Magazine App》改出了一个比较适用于聊天内容展现的图文混排(文字和表情)控件。






- - (void)appendAttachment: (M80AttributedLabelAttachment *)attachment
- {
- attachment.fontAscent = _fontAscent;
- attachment.fontDescent = _fontDescent;
- unichar objectReplacementChar = 0xFFFC;
- NSString *objectReplacementString = [NSString stringWithCharacters:&objectReplacementChar length:1];
- NSMutableAttributedString *attachText = [[NSMutableAttributedString alloc]initWithString:objectReplacementString];
- CTRunDelegateCallbacks callbacks;
- callbacks.version = kCTRunDelegateVersion1;
- callbacks.getAscent = ascentCallback;
- callbacks.getDescent = descentCallback;
- callbacks.getWidth = widthCallback;
- callbacks.dealloc = deallocCallback;
- CTRunDelegateRef delegate = CTRunDelegateCreate(&callbacks, (void *)attachment);
- NSDictionary *attr = [NSDictionary dictionaryWithObjectsAndKeys:(__bridge id)delegate,kCTRunDelegateAttributeName, nil];
- [attachText setAttributes:attr range:NSMakeRange(0, 1)];
- CFRelease(delegate);
- [_attachments addObject:attachment];
- [self appendAttributedText:attachText];
- }
- CGFloat ascentCallback(void *ref)
- {
- M80AttributedLabelAttachment *image = (__bridge M80AttributedLabelAttachment *)ref;
- CGFloat ascent = 0;
- CGFloat height = [image boxSize].height;
- switch (image.alignment)
- {
- case M80ImageAlignmentTop:
- ascent = image.fontAscent;
- break;
- case M80ImageAlignmentCenter:
- {
- CGFloat fontAscent = image.fontAscent;
- CGFloat fontDescent = image.fontDescent;
- CGFloat baseLine = (fontAscent + fontDescent) / 2 - fontDescent;
- ascent = height / 2 + baseLine;
- }
- break;
- case M80ImageAlignmentBottom:
- ascent = height - image.fontDescent;
- break;
- default:
- break;
- }
- return ascent;
- }
- CGFloat descentCallback(void *ref)
- {
- M80AttributedLabelAttachment *image = (__bridge M80AttributedLabelAttachment *)ref;
- CGFloat descent = 0;
- CGFloat height = [image boxSize].height;
- switch (image.alignment)
- {
- case M80ImageAlignmentTop:
- {
- descent = height - image.fontAscent;
- break;
- }
- case M80ImageAlignmentCenter:
- {
- CGFloat fontAscent = image.fontAscent;
- CGFloat fontDescent = image.fontDescent;
- CGFloat baseLine = (fontAscent + fontDescent) / 2 - fontDescent;
- descent = height / 2 - baseLine;
- }
- break;
- case M80ImageAlignmentBottom:
- {
- descent = image.fontDescent;
- break;
- }
- default:
- break;
- }
- return descent;
- }
- CGFloat widthCallback(void* ref)
- {
- M80AttributedLabelAttachment *image = (__bridge M80AttributedLabelAttachment *)ref;
- return [image boxSize].width;
- }
- - (void)drawAttachments
- {
- if ([_attachments count] == 0)
- {
- return;
- }
- CGContextRef ctx = UIGraphicsGetCurrentContext();
- if (ctx == nil)
- {
- return;
- }
- CFArrayRef lines = CTFrameGetLines(_textFrame);
- CFIndex lineCount = CFArrayGetCount(lines);
- CGPoint lineOrigins[lineCount];
- CTFrameGetLineOrigins(_textFrame, CFRangeMake(0, 0), lineOrigins);
- NSInteger numberOfLines = [self numberOfDisplayedLines];
- for (CFIndex i = 0; i < numberOfLines; i++)
- {
- CTLineRef line = CFArrayGetValueAtIndex(lines, i);
- CFArrayRef runs = CTLineGetGlyphRuns(line);
- CFIndex runCount = CFArrayGetCount(runs);
- CGPoint lineOrigin = lineOrigins[i];
- CGFloat lineAscent;
- CGFloat lineDescent;
- CTLineGetTypographicBounds(line, &lineAscent, &lineDescent, NULL);
- CGFloat lineHeight = lineAscent + lineDescent;
- CGFloat lineBottomY = lineOrigin.y - lineDescent;
- // Iterate through each of the "runs" (i.e. a chunk of text) and find the runs that
- // intersect with the range.
- for (CFIndex k = 0; k < runCount; k++)
- {
- CTRunRef run = CFArrayGetValueAtIndex(runs, k);
- NSDictionary *runAttributes = (NSDictionary *)CTRunGetAttributes(run);
- CTRunDelegateRef delegate = (__bridge CTRunDelegateRef)[runAttributes valueForKey:(id)kCTRunDelegateAttributeName];
- if (nil == delegate)
- {
- continue;
- }
- M80AttributedLabelAttachment* attributedImage = (M80AttributedLabelAttachment *)CTRunDelegateGetRefCon(delegate);
- CGFloat ascent = 0.0f;
- CGFloat descent = 0.0f;
- CGFloat width = (CGFloat)CTRunGetTypographicBounds(run,
- CFRangeMake(0, 0),
- &ascent,
- &descent,
- NULL);
- CGFloat imageBoxHeight = [attributedImage boxSize].height;
- CGFloat xOffset = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, nil);
- CGFloat imageBoxOriginY = 0.0f;
- switch (attributedImage.alignment)
- {
- case M80ImageAlignmentTop:
- imageBoxOriginY = lineBottomY + (lineHeight - imageBoxHeight);
- break;
- case M80ImageAlignmentCenter:
- imageBoxOriginY = lineBottomY + (lineHeight - imageBoxHeight) / 2.0;
- break;
- case M80ImageAlignmentBottom:
- imageBoxOriginY = lineBottomY;
- break;
- }
- CGRect rect = CGRectMake(lineOrigin.x + xOffset, imageBoxOriginY, width, imageBoxHeight);
- UIEdgeInsets flippedMargins = attributedImage.margin;
- CGFloat top = flippedMargins.top;
- flippedMargins.top = flippedMargins.bottom;
- flippedMargins.bottom = top;
- CGRect attatchmentRect = UIEdgeInsetsInsetRect(rect, flippedMargins);
- id content = attributedImage.content;
- if ([content isKindOfClass:[UIImage class]])
- {
- CGContextDrawImage(ctx, attatchmentRect, ((UIImage *)content).CGImage);
- }
- else if ([content isKindOfClass:[UIView class]])
- {
- UIView *view = (UIView *)content;
- if (view.superview == nil)
- {
- [self addSubview:view];
- }
- CGRect viewFrame = CGRectMake(attatchmentRect.origin.x,
- self.bounds.size.height - attatchmentRect.origin.y - attatchmentRect.size.height,
- attatchmentRect.size.width,
- attatchmentRect.size.height);
- [view setFrame:viewFrame];
- }
- else
- {
- NSLog(@"Attachment Content Not Supported %@",content);
- }
- }
- }
- }
[转] iOS文字排版(CoreText)那些事儿的更多相关文章
- iOS:基于CoreText的排版引擎
一.CoreText的简介 CoreText是用于处理文字和字体的底层技术.它直接和Core Graphics(又被称为Quartz)打交道.Quartz是一个2D图形渲染引擎,能够处理OSX和iOS ...
- iOS开发-UITextView文字排版
UITextView文本排版 1.配置NSMutableParagraphStyle NSMutableParagraphStyle *MParaStyle = [[NSMutableParagrap ...
- (转)iOS7界面设计规范(10) - UI基础 - 文字排版与配色
明天就是周四了.貌似前几天还在恨周一呢.话说今天几乎开了一整天的会,正经事情没做多少:这种感觉比一整天从早到晚12个小时的忙碌于一件事情还要让人感到疲惫的对叭?那今天的iOS7设计规范更新又是一篇很简 ...
- amazeui学习笔记--css(基本样式3)--文字排版Typography
amazeui学习笔记--css(基本样式3)--文字排版Typography 一.总结 1.字体:amaze默认非 衬线字体(sans-serif) 2.引用块blockquote和定义列表:引用块 ...
- OpenJudge计算概论-文字排版
/*====================================================================== 文字排版 总时间限制: 1000ms 内存限制: 65 ...
- div介绍 盒子模型边框属性 CSS初始化 文字排版 边框调整 溢出
今天学习的div,了解了div是干什么用的掌握了什么是盒子模型,以及div的外边距内边距以及边框,运用div和CSS给文字排版,利用边框的来做图像,div溢出的处理 CSS初始化: 精确排版的时候用这 ...
- 【html】文字排版
Web开发过程中文字排版,默认的情况下,行末的长单词会撑开容器. 我们想要的是(像word一样.能够自动换行.既不撑大容器.也不强制拆开行末单词.并且不会隐藏行末单词的多余字母) ①不能撑开容器 ②完 ...
- bootstrap世界探索1——山川河流(文字排版)
世界到底是什么?其实世界很简单,正所谓一花一世界,一树一菩提,世界就在我们身边.造物神是伟大的,在我看来无论是HTML,css,js都可以看作是一个世界,但是他们是构成宏观世界不可或缺的,正如IU框架 ...
- iOS App开发的那些事儿2:如何搭建合适的框架
<iOS App开发的那些事儿>系列文章从更宏观的角度出发,不仅仅局限于具体某个功能.界面的实现,而是结合网易云信iOS端研发负责人多年的经验,从如何优化现有代码的角度出发,深度分析如何创 ...
随机推荐
- libnetwork插件化网络功能
Docker把网络跟存储这两部分的功能实现都以插件化形式剥离出来,允许用户通过指令来选择不同的后端实现.这也是Docker希望构建围绕着容器的强大生态系统的一些积极的尝试.剥离出来的独立容器网络项目叫 ...
- MySQL命令行导入导出数据
参考:http://www.cnblogs.com/xcxc/archive/2013/01/30/2882840.html 这篇文章写得非常好,又简洁,而且深入浅出,排版也非常好看,不会像网上的只是 ...
- Innosetup的状态页面和向导页面解释
1.安装: CurStepChanged所对应的全部状态:3种 1.1. CurStep=ssInstall --是在程序实际安装前(所有配置都准备好了) 1.2. CurSt ...
- scanf()函数分析
首先,先来讲一下scanf的读取流程: 从键盘输入的都是字符类型(一系列的字符),scanf()的作用就是将这个字符序列转换成一个或多个指定的类型,并保存到变量中. 从键盘输入的字符序列会先缓存到键盘 ...
- 面试题28:单链表一次遍历删除从后往前的第n个节点
class Solution { public: ListNode *removeNthFromEnd(ListNode *head, int n) { ListNode* fake = ); fak ...
- 01 JDBC的问题
jdbc编程步骤: 1. 加载.注册数据库驱动 DriverManager 2. 创建并获取数据库链接 Connection 3. 创建jdbc statement/preparedState ...
- SpringBoot入门 (六) 数据库访问之Mybatis
本文记录学习在SpringBoot中使用Mybatis. 一 什么是Mybatis MyBatis 是一款优秀的持久层框架,它支持定制化 SQL.存储过程以及高级映射.MyBatis 避免了几乎所有的 ...
- 国际化实现之基于jquery
jQuery.i18n.properties是一款轻量级的jQuery国际化插件,能实现Web前端的国际化. jQuery.i18n.properties 采用 .properties 文件对 Jav ...
- bzoj 2138: stone
Description 话说Nan在海边等人,预计还要等上M分钟.为了打发时间,他玩起了石子.Nan搬来了N堆石子,编号为1到N,每堆 包含Ai颗石子.每1分钟,Nan会在编号在[Li,Ri]之间的石 ...
- ping过程详细解读
0. 前言 在讲解ping过程之前,我们需要了解以下概念. 1). 何为ping PING (Packet Internet Groper),因特网包探索器,用于测试网络连通性的程序. Pin ...