当前位置: 首页 > 编程日记 > 正文

CoreText入坑一

CoreText是Mac OS和iOS系统中处理文本的low-level API, 不管是使用OC还是swift, 实际我们使用CoreText都还是间接或直接使用C语言在写代码。CoreText是iOS和Mac OS中文本处理的根基, TextKit和WebKit都是构建于其上。

一. 基础

1.在使用CoreText编写代码之前, 需要先了解一些基础知识。下图是CoreText的基础框架


  • CTFrame可以想象成画布, 画布的大小范围由CGPath决定
  • CTFrame由很多CTLine组成, CTLine表示为一行
  • CTLine由多个CTRun组成, CTRun相当于一行中的多个块, 但是CTRun不需要你自己创建, 由NSAttributedString的属性决定, 系统自动生成。每个CTRun对应不同属性
  • CTFramesetter是一个工厂, 创建CTFrame, 一个界面上可以有多个CTFrame

2.文字的样式包括很多, 而每个字符的显示要归功于字体, 而字体包括很多基础知识, 比如磅值, 样式, 基线, 连字等等, 这里就不做更多介绍, 推荐两篇文章阅读。
CoreText基础概念
CoreText入门

在这里, 贴出CoreText基础概念文中关于字体结构的图(图片版权归此文作者)



二. 使用基本步骤

新建一个UIView, 在view的drawRect函数中按步骤写入下面的代码

  • 1.获取当前上下文
let context = UIGraphicsGetCurrentContext()
  • 2.转换坐标系
CGContextSetTextMatrix(context, CGAffineTransformIdentity)
CGContextTranslateCTM(context, 0, self.bounds.size.height)
CGContextScaleCTM(context, 1.0, -1.0)
  • 3.初始化路径
let path = CGPathCreateWithRect(self.bounds, nil)
  • 4.初始化字符串
let attrString = NSMutableAttributedString(string: "Hello CoreText")
  • 5.初始化framesetter
let framesetter = CTFramesetterCreateWithAttributedString(attrString)
  • 6.绘制frame
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attrString.length), path, nil)
CTFrameDraw(frame, context!)

绘制的步骤完成了, 然后在ViewController里面将此view加入到ViewController中, 记得将view的背景色设置为白色, 那么效果就应该如下了



文字绘制出来了, 这就是CoreText使用最基本的步骤了。

三.简单的富文本Label

上面简单的绘制步骤中, 最后一步绘制frame, 是将整个frame当做一块绘制, 至于什么换行, 行中的样式什么的都是系统自己决定了。在开始之前, 我们将这个绘制frame改成我们自己一行一行, 甚至一个run一个run的绘制

  • 按行绘制
// 1.获得CTLine数组
let lines = CTFrameGetLines(frame)// 2.获得行数
let numberOfLines = CFArrayGetCount(lines)// 3.获得每一行的origin, CoreText的origin是在字形的baseLine处的, 请参考字形图
var lineOrigins = [CGPoint](count: numberOfLines, repeatedValue: CGPointZero)
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), &lineOrigins)// 4.遍历每一行进行绘制
for index in 0..<numberOfLines {let origin = lineOrigins[index]// 参考: http://swifter.tips/unsafe/let line = unsafeBitCast(CFArrayGetValueAtIndex(lines, index), CTLine.self)// 设置每一行的位置CGContextSetTextPosition(context, origin.x, origin.y)// 开始一行的绘制CTLineDraw(line, context)
}

将最后一步改成按行绘制, 最终得到的效果也和按frame绘制一样的, 接下来看下按Run绘制

  • 按Run绘制
// 画一行
func drawLine(line: CTLine, context: CGContext) {let runs = CTLineGetGlyphRuns(line) as Arrayruns.forEach { run inCTRunDraw(run as! CTRun, context, CFRangeMake(0, 0))}}
}

用此函数替换CTLineDraw(line, context)这一句就可以了, 效果也如上面。

那么接下来实现一个简单的富文本Label, 将上面的view改名为TULabel

  • 声明一个富文本变量给此Label
var attributedText: NSAttributedString?
  • 将上面的第4步注释掉, 初始化framesetter的字符串直接传入此变量, 至于后面的绘制你可以用任意一种, 这样TULabel就可以实现一部分富文本了, 在controller中创建一个TULabel, 然后来个NSMutableAttributedString实例赋值给TULabel.attributedText, 下面列出此时可用的富文本样式
let attributedText = NSMutableAttributedString(string: ...)// CoreText支持的属性// 字体颜色
attributedText.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, 10))// 下划线
let underlineStyles = [NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue,NSUnderlineColorAttributeName: UIColor.orangeColor()]
attributedText.addAttributes(underlineStyles, range: NSMakeRange(10, 10))// 字体
attributedText.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFontOfSize(50), range: NSMakeRange(20, 10))// 描边(Stroke):组成字符的线或曲线。可以加粗或改变字符形状
let strokeStyles = [NSStrokeWidthAttributeName: 10,NSStrokeColorAttributeName: UIColor.blueColor()]
attributedText.addAttributes(strokeStyles, range: NSMakeRange(40, 20))// 横竖文本
attributedText.addAttribute(NSVerticalGlyphFormAttributeName, value: 0, range: NSMakeRange(70, 10))// 字符间隔
attributedText.addAttribute(NSKernAttributeName, value: 5, range: NSMakeRange(90, 10))// 段落样式
let paragraphStyle = NSMutableParagraphStyle()//对齐模式
paragraphStyle.alignment = .Center//换行裁剪模式
paragraphStyle.lineBreakMode = .ByWordWrapping// 行间距
paragraphStyle.lineSpacing = 5.0// 字符间距
paragraphStyle.paragraphSpacing = 2.0attributedText.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSRange(location: 0, length: attributedText.length))

此时, 你就会看到如下效果


  • 效果出来了, 你是否就满足了。那其他平常可以使用的样式要怎么样使用CoreText来实现了? 我们就先实现一个样式--删除线, 将上面的drawLine函数改成如下
// 画一行
func drawLine(line: CTLine, context: CGContext) {let runs = CTLineGetGlyphRuns(line) as Arrayruns.forEach { run inCTRunDraw(run as! CTRun, context, CFRangeMake(0, 0))// 获得run的所有样式let attributes = CTRunGetAttributes(run as! CTRun) as NSDictionary// 判断是run是否含有删除线样式if nil != attributes[NSStrikethroughStyleAttributeName] {// 开始画删除线drawStrikethroughStyle(run as! CTRun, attributes: attributes, context: context)}}  
}
  • 当然, 你要将CTLineDraw(line, context)换成自定义的画行函数drawLine(line, context: context), 那么接下来就是画删除线了
// 画删除线, 这里涉及到字体相关知识, 请参考第二节, 画删除线实际画在字的中间, 而字体的高度不一样, 实际是画在x高度的一半位置
func drawStrikethroughStyle(run: CTRun, attributes: NSDictionary, context: CGContext) {// 1.获取删除线样式let styleRef = attributes[NSStrikethroughStyleAttributeName]var style: NSUnderlineStyle = .StyleNoneCFNumberGetValue(styleRef as! CFNumber, CFNumberType.SInt64Type, &style)// 如果定义为none, 就不用画了guard style != .StyleNone else {return}// 2.获得画线的宽度var lineWidth: CGFloat = 1if (style.rawValue & NSUnderlineStyle.StyleThick.rawValue) == NSUnderlineStyle.StyleThick.rawValue {lineWidth *= 2}CGContextSetLineWidth(context, lineWidth)// 3.获取画线的起点var firstPosition = CGPointZerolet firstGlyphPosition = CTRunGetPositionsPtr(run)if nil == firstGlyphPosition {let positions = UnsafeMutablePointer<CGPoint>.alloc(1)positions.initialize(CGPointZero)CTRunGetPositions(run, CFRangeMake(0, 0), positions)firstPosition = positions.memorypositions.destroy()} else {firstPosition = firstGlyphPosition.memory}// 4.我们要开始画线了CGContextBeginPath(context)// 5.获取定义的线的颜色, 默认为黑色let lineColor = attributes[NSStrikethroughColorAttributeName]if nil == lineColor {CGContextSetStrokeColorWithColor(context, UIColor.blackColor().CGColor)} else {CGContextSetStrokeColorWithColor(context, (lineColor as! UIColor).CGColor)}// 6.字体高度, 中间位置为x高度的一半let font = attributes[NSFontAttributeName] ?? UIFont.systemFontOfSize(UIFont.systemFontSize())var strikeHeight: CGFloat = font.xHeight / 2.0 + firstPosition.y// 多行调整let pt = CGContextGetTextPosition(context)strikeHeight += pt.y// 画线的宽度let typographicWidth = CGFloat(CTRunGetTypographicBounds(run, CFRangeMake(0, 0), nil, nil, nil))// 7.开始画线CGContextMoveToPoint(context, pt.x + firstPosition.x, strikeHeight)CGContextAddLineToPoint(context, pt.x + firstPosition.x + typographicWidth, strikeHeight)CGContextStrokePath(context)
}
  • 然后在controller中给attributedText添加删除线样式
// 删除线
let strikethroughStyle = [NSStrikethroughStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue,NSStrikethroughColorAttributeName: UIColor.cyanColor()]
attributedText.addAttributes(strikethroughStyle, range: NSMakeRange(150, 20))

这样就实现了删除线样式效果了


至此, 删除线的样式就完成了, 其他样式将可能在下一篇CoreText文章中实现。
源码在此, 请参考源码中的CoreText/1文件夹!!!

参考:
CoreText基础概念
CoreText入门
Nimbus

本文由啸寒原创, 转载请注明出处!!!


相关文章:

mysql连接hang住问题分析

【问题现象】&#xff1a; 1. Linuxc多线程连接mysql数据库&#xff0c;每次都是短连接&#xff0c;操作完后就释放连接&#xff0c;有时候会出现mysql_real_connect挂住的现象 2. 挂住超时mysql_real_connect返回后报错如下&#xff1a;Lostconnection to MySQL s…

【Linux学习笔记】 -- 基本Shell命令

常见的目录名均基于文件系统层级标准(filesystem hierarchy standard&#xff0c;FHS) Linux的四个部分&#xff1a; 1 Linux内核&#xff1a;控制所有硬软件&#xff0c;必要时分配硬件根据需要执行软件 系统内存管理&#xff1a;可用物理内存 创建、管理虚拟内存[交换空间…

【OpenCV】图像代数运算:平均值去噪,减去背景

代数运算&#xff0c;就是对两幅图像的点之间进行加、减、乘、除的运算。四种运算相应的公式为&#xff1a; 代数运算中比较常用的是图像相加和相减。图像相加常用来求平均值去除addtive噪声或者实现二次曝光&#xff08;double-exposure&#xff09;。图像相减用于减去背景或周…

简明 Vim 练级攻略(转)

vim的学习曲线相当的大&#xff08;参看各种文本编辑器的学习曲线&#xff09;&#xff0c;所以&#xff0c;如果你一开始看到的是一大堆VIM的命令分类&#xff0c;你一定会对这个编辑器失去兴趣的。下面的文章翻译自《Learn Vim Progressively》&#xff0c;我觉得这是给新手最…

iOS 的离屏渲染

原文链接&#xff1a;http://www.imlifengfeng.com/blog/?p593OpenGL ES 是一套多功能开放标准的用于嵌入系统的 C-based 的图形库&#xff0c;用于 2D 和 3D 数据的可视化。OpenGL 被设计用来转换一组图形调用功能到底层图形硬件&#xff08;GPU&#xff09;&#xff0c;由 G…

MySQL 常见操作指令

什么是SQL&#xff1f; SQL&#xff08;Structured Query Language&#xff09;用于访问和操作数据库的结构化查询语言。 数据库包含一个或多个表&#xff0c;每个表均有名称标识&#xff0c;包含数据的记录&#xff08;行&#xff09;。 典型的SQL语句 1. SELEC语句 SELE…

iOS 实现点击微信头像效果

来源&#xff1a;伯乐在线 - 小良 如有好文章投稿&#xff0c;请点击 → 这里了解详情 如需转载&#xff0c;发送「转载」二字查看说明 公司产品需要实现点击个人主页头像可以放大头像、缩放头像、保存头像效果&#xff08;和点击微信个人头像类似&#xff09;&#xff0c;故找…

HDU 4292 Food(dinic +拆点)

题目链接 我做的伤心了&#xff0c;不知是模版效率低&#xff0c;还是错了&#xff0c;交上就是TLE&#xff0c;找了份别人的代码&#xff0c;改了好几下终于过了。。 1 #include <cstdio>2 #include <cstring>3 #include <queue>4 #include <map>5 #i…

jQuery中用ajax访问php接口文件

js代码 function ajax_request(){var result;var articleId new Object();articleIdgetArticleId();$.ajax({url: "/topicPage/getComment.php",//请求php文件的路径data:{id:articleId},//请求中要传送的参数,会自动拼接成一个路径&#xff0c;在php中用get方式获取…

Python 数据库操作 psycopg2

文章目录安装基本使用安装 psycopg 是 Python 语言中 PostpreSQL数据库接口 安装环境&#xff1a; Python&#xff1a;v2.7, v3.4~3.8PostGreSQL&#xff1a;7.4~12 pip install psycopg2基本使用 import psycopg2def connect_db(host: str,port: int,database: str,user:…

Android logcat命令详解

一、logcat命令介绍 1.android log系统 2.logcat介绍 logcat是android中的一个命令行工具&#xff0c;可以用于得到程序的log信息 log类是一个日志类&#xff0c;可以在代码中使用logcat打印出消息 常见的日志纪录方法包括&#xff1a;方法 描述 v(String,String) (vervbose)显…

[iOS]如何重新架构 JPVideoPlayer ?

注意&#xff1a;此文为配合 JPVideoPlayer version 2.0 版本发布而写&#xff0c;如果你想了解 2.0 版本的更新内容和所有实现细节&#xff0c;请点击前往 GitHub。 导言&#xff1a;我几个月前写了一个在 UITableView 中滑动 UITableViewCell 播放视频的框架&#xff0c;类似…

函数项目一个超感人的故事:关于swfupload在某些环境下面session丢失的完美解决方案(看完我哭了)...

查了好多资料&#xff0c;发现还是不全&#xff0c;干脆自己整理吧&#xff0c;至少保证在我的做法正确的&#xff0c;以免误导读者&#xff0c;也是给自己做个记录吧&#xff01; 标题吸引到你了吗&#xff1f; 先说一下这个题问成形的原因。大家都晓得 session是靠cookie中的…

【学习笔记】git 使用文档

安装 git # mac 环境 brew install git检查是否安装成功 ➜ ~ git --version git version 2.20.1 (Apple Git-117)卸载 git ➜ ~ which -a git /usr/bin/git ➜ ~ cd /usr/bin ➜ bin sudo rm -rf git*git init 命令 对一个空文件&#xff0c;git 初始化。文件名称增加…

UIBezierPath和CAShapeLayer创建不规则View(Swift 3.0)

最近一个朋友在做图片处理的 App&#xff0c;想要实现类似 MOLDIV App 拼图的UI效果&#xff08;如何创建不规则的 view&#xff09;&#xff0c;就问我有什么想法。我首先想到的就是 UIBezierPathCAShapeLayer的方式&#xff0c;为了验证自己的想法&#xff0c;写了一个小 dem…

http响应状态

Servlet API&#xff1a; javax.servlet.http.HttpServletResponse 用于创建HTTP响应&#xff0c;包括HTTP协议的状态行、响应头以及消息体 HTTP状态码&#xff1a; 100-199&#xff1a;表示信息性代码&#xff0c;标示客户端应该采取的其他动作&#xff0c;请求正在进行。 200…

antlr.collections.AST.getLine()I问题的起因及解决

在我们的java web 项目中引入hibernate和struts&#xff0c;当我们使用HQL语句进行查询时会报 antlr.collections.AST.getLine()I的错误&#xff0c;导致程序无法继续运行&#xff0c;这并不是我们的程序写的有错误&#xff0c;出现这个异常的原因是因为我们使用的hibernate和s…

2018湖湘杯海选复赛Writeup

2018湖湘杯Writeup0x01 签到题0x02 MISC Flow0x03 WEB Code Check0x04 WEB Readflag0x05 WEB XmeO0x06 Reverse Replace0x07 MISC Disk0x08 Crypto Common Crypto0x09 Reverse HighwayHash640x10 Web Mynot0x01 签到题 关注合天智汇公众号&#xff0c;回复hxb2018得到flag。0x…

Operation Queues并发编程

并发、异步在我们的编程中&#xff0c;见到的太多了。在iOS中&#xff0c;实现并发的主要三个途径Operation Queues、Dispatch Queues、Dispatch Sources&#xff0c;今天我们就来详细介绍Operatin Queues的使用&#xff0c;花了两天时间写这一篇&#xff0c;值得一看。 为什么…

socket 服务器浏览器与服务器客户端实例

一、服务器与浏览器 // 取得本机的loopback网络地址&#xff0c;即127.0.0.1 IPAddress address IPAddress.Loopback; IPEndPoint endPoint new IPEndPoint(address, 49152); Socket socket new Socket(AddressFamily.InterNetwork, Socke…

匹配3位或4位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔...

public bool IsPhone(string input){string pattern "^\\(0\\d{2}\\)[- ]?\\d{8}$|^0\\d{2}[- ]?\\d{8}$|^\\(0\\d{3}\\)[- ]?\\d{7}$|^0\\d{3}[- ]?\\d{7}$";Regex regex new Regex(pattern);return regex.IsMatch(input);} 转载于:https://www.cnblogs.com/…

Mac MySQL配置环境变量的两种方法

第一种&#xff1a; 1.打开终端,输入&#xff1a; cd ~ 会进入~文件夹 2.然后输入&#xff1a;touch .bash_profile 回车执行后&#xff0c; 3.再输入&#xff1a;open -e .bash_profile 会在TextEdit中打开这个文件&#xff08;如果以前没有配置过环境变量&#xff0c;那么这…

linux之x86裁剪移植---字符界面sdl开发入门

linux下有没有TurboC2.0那样的画点、线、圆的图形函数库&#xff0c;有没有grapihcs.h&#xff0c;或者与之相对应或相似的函数库是什么&#xff1f;有没有DirectX这样的游戏开发库&#xff1f;SDL就是其中之一。SDL&#xff08;Simple DirectMedia Layer&#xff09;是一个夸平…

iOS 视频捕获系列Swift之AVFoundation(一)

iOS 视频捕获系列之AVFoundation(一) AVCaptureMovieFileOutput系列 在iOS开发过程中&#xff0c;或多或少的都涉及视频的操作。 尤其在去年直播行业的带动下&#xff0c;移动端对视频的处理也愈来愈发要求严格。 本文也是在 这篇 中参考而来。 Swift 版本哦&#xff01; 本文 …

C#做外挂常用API

using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServices; //这个肯定要的 namespace WindowsApplication1 {class win32API{public const int OPEN_PROCESS_ALL 2035711;public const int PAGE_READWRITE 4;public con…

phpinfo 信息利用

0x01 基础信息 1.system info:提供详细的操作系统信息&#xff0c;为提权做准备。 2.extension_dir:php扩展的路径 3.$_SERVER[‘HTTP_HOST’]:网站真实IP、CDN什么的都不存在的&#xff0c;找到真实ip&#xff0c;扫一扫旁站&#xff0c;没准就拿下几个站。 4.$_SERVER[‘…

iOS三种录制视频方式详细对比

先附上参考资料 http://www.jianshu.com/p/16cb14f53933 https://developer.apple.com/library/content/samplecode/AVSimpleEditoriOS/Introduction/Intro.html https://github.com/objcio/VideoCaptureDemo https://github.com/gsixxxx/DTSmallVideo https://github.com/Andy…

C# 实现Oracle中的数据与Excel之间的转换

最近项目要求实现数据库之间数据在各个数据库之间导入导出&#xff0c;在此做个笔记 1. 将Oracle中的表导入到Excel中&#xff0c;反之亦然 private static readonly string connectionString ConfigurationManager.ConnectionStrings["OracleConnection"].Connecti…

【转】Word2007中不连续页码设置 多种页码设置

【转】Word2007中不连续页码设置 多种页码设置 页码是论文必不可少的部分。我们看一下如何添加页码&#xff0c;并且针对一些特殊的格式要求怎么应对&#xff1a; 如果是【毕业论文】有多种混合页码&#xff0c;有Ⅰ、Ⅱ、Ⅲ。。。还有1、2、3 。。。请直接看【第二种方法】。 …

vim编辑器异常退出产生备份文件

当非正常关闭vim编辑器时&#xff08;比如直接关闭终端或者电脑断电&#xff09;&#xff0c;会生成一个.swp文件&#xff0c;这个文件是一个临时交换文件&#xff0c;用来备份缓冲区中的内容。 需要注意的是如果你并没有对文件进行修改&#xff0c;而只是读取文件&#xff0c…