coretext简单使用
相对于UIKit,使用coretext绘制文本效率高,具有更高的自由度,可随时插入图片,增加文本点击事件等。
1.增加文本的点击事件
思路:定义UILabel子类,设置可点击的富文本range及其他属性(颜色、字体),touchBegin方法中根据点击位置判断所在行所在index,最后判断index是否在range内,若在,则响应事件。
首先定义可点击的文本model,该model主要含有3个属性,string-用于回调显示,range-用于判断位置,attributes-用于绘制文本
class LinkAttributesModel:NSObject {
var string:String!
var range:NSRange!
var attributes:Dictionary<NSAttributedStringKey,Any>!
}
接着在label子类实现方法setString(string:String,attributes:Dictionary<NSAttributedStringKey,Any>?,linkArr:Array<LinkAttributesModel>),第一个参数是显示string,第二个参数是string的属性,第三个参数是一个可点击文本的集合,需要手动赋值
// 声明
func setString(string:String,attributes:Dictionary<NSAttributedStringKey,Any>?,linkArr:Array<LinkAttributesModel>) {
locLinkArr = linkArr;
let attributedString = NSMutableAttributedString(string: string, attributes: attributes)
for linkMdl in linkArr {
attributedString.addAttributes(linkMdl.attributes, range: linkMdl.range)
}
// self.attributedText = attributedString
tmpAttributedString = attributedString // create frameRef
let frameSetter = CTFramesetterCreateWithAttributedString(attributedString)
let path = CGMutablePath()
path.addRect(self.bounds)
frameRef = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, string.count), path, nil) self.isUserInteractionEnabled = true
} // 调用
// 构造属性model
var mdlArr = [LinkAttributesModel]()
for i in 0...2 {
let mdl = LinkAttributesModel()
let loc = i*10
let range = NSMakeRange(loc, 5)
mdl.range = range
let startIdx = showString.index(showString.startIndex, offsetBy: range.location)
let endIdx = showString.index(startIdx, offsetBy: range.length)
mdl.string = String(showString[startIdx...endIdx])
let randomColor = UIColor(red: CGFloat(arc4random_uniform(256))/255.0, green: CGFloat(arc4random_uniform(256))/255.0, blue: CGFloat(arc4random_uniform(256))/255.0, alpha: 1)
mdl.attributes = [NSAttributedStringKey.foregroundColor:randomColor]
mdlArr.append(mdl)
} let lbl = Taplabel(frame:self.view.bounds.insetBy(dx: 0, dy: 50))
let style = NSMutableParagraphStyle()
style.lineSpacing = 10
lbl.setString(string: showString, attributes: [NSAttributedStringKey.font:UIFont.systemFont(ofSize: 20),NSAttributedStringKey.paragraphStyle:style], linkArr: mdlArr)
最后根据属性文本创建对应的frameRef,然后遍历其CTLine数组,判断点击的位置在哪行内,再根据x获取当前行的偏移位置index,最后遍历linkAttributesModel,判断range是否包含index,如果包含,则触发回调事件
func touchEvents(pt:CGPoint) {
if (frameRef == nil) {return}
let lines:[CTLine] = CTFrameGetLines(frameRef!) as! [CTLine]
let lineCount = lines.count
if (lineCount == 0) {return}
// let flipTransform = CGAffineTransform(translationX: 0, y: self.bounds.size.height).scaledBy(x: 1, y: -1)
for i in 0..<lineCount {
let line = lines[i]//CFArrayGetValueAtIndex(lines, i) as! CTLine
var ascent:CGFloat = 0
var descent:CGFloat = 0
var leading:CGFloat = 0
let width = CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, &leading))
let height = ascent + abs(descent) + leading var rect = CGRect(x: pt.x, y: pt.y, width: width, height: height)
// var rect = flippedRect.applying(flipTransform) var lineSpace:CGFloat = 0;
if let style = tmpAttributedString?.attribute(NSAttributedStringKey.paragraphStyle, at: 0, effectiveRange: nil) as? NSParagraphStyle{
lineSpace = style.lineSpacing
} rect.origin.y = (height+lineSpace)*CGFloat(i); if rect.contains(pt) {
var stringIndex = CTLineGetStringIndexForPosition(line, pt)
var offset:CGFloat = 0
CTLineGetOffsetForStringIndex(line, stringIndex, &offset)
if (offset > pt.x) {
stringIndex = stringIndex - 1
} for linkMdl in locLinkArr! {
if linkMdl.range.contains(stringIndex) {
tapBlock!(linkMdl.string)
}
}
}
}
}
上述代码的主要核心在于
a.算行高 CTLineGetTypographicBounds(line,&asent,&descent,&leading)
b.算行内偏移 CTLineGetStringIndexForPosition(line,pt) 如果根据该index算出的偏移量大于pt.x,则index-1
c.判断range包含 range.contains(index)
2.图文绘制
思路:NSAttributedString中插入占位空白符-关联图片属性。图片宽高由CTRunDelegate,图片可藏于追加属性中。ctx.draw(image,in:drawRect)时遍历CTLine,寻找含有CTRunDelegate的CTRun,如果存在,则获取图片的相应数组,构造drawRect和image绘制图片。
追加图片属性
func appendImage(attrStr:NSAttributedString)->NSAttributedString {
var callBacks = CTRunDelegateCallbacks(version: kCTRunDelegateVersion1, dealloc: { (pointer) in
}, getAscent: { (ref) -> CGFloat in
let dic = ref.assumingMemoryBound(to: NSDictionary.self)
return dic.pointee.object(forKey: "height") as! CGFloat
}, getDescent: { (ref) -> CGFloat in
return 0
}) { (ref) -> CGFloat in
let dic = ref.assumingMemoryBound(to: NSDictionary.self)
return dic.pointee.object(forKey: "width") as! CGFloat
} let imageData:Dictionary<String,Any> = ["height":100,"width":200]
let imageDataPointer = UnsafeMutablePointer<Dictionary<String,Any>>.allocate(capacity: 1)
imageDataPointer.initialize(to: imageData)
let runDelegate = CTRunDelegateCreate(&callBacks, imageDataPointer) let runAttributes:[NSAttributedStringKey:Any] = [kCTRunDelegateAttributeName as NSAttributedStringKey:runDelegate as Any]
let runAttributeStr = NSMutableAttributedString(string: " ", attributes: runAttributes)
runAttributeStr.addAttribute(NSAttributedStringKey(rawValue: "imageName"), value: "shot", range: NSMakeRange(0, 1)) let mutableAttrStr = NSMutableAttributedString(attributedString: attrStr)
let whiteSpaceStr = createAttributedString(str: "临时换行\n")
let suffixStr = createAttributedString(str: "\nhaha insert image succeed")
mutableAttrStr.append(whiteSpaceStr)
mutableAttrStr.append(runAttributeStr)
mutableAttrStr.append(suffixStr)
return mutableAttrStr
}
绘制追加的图片属性
func drawImage(frame:CTFrame,ctx:CGContext) {
let lines = CTFrameGetLines(frame) as Array
var lineOrigins:Array<CGPoint> = Array(repeating: .zero, count: lines.count)
CTFrameGetLineOrigins(frame, CFRange(location: 0, length: 0), &lineOrigins)
for i in 0..<lines.count {
let line = lines[i] as! CTLine
let runs = CTLineGetGlyphRuns(line) as Array
print("lineCount == \(lines.count) i == \(i)\n")
for j in 0..<runs.count {
let run = runs[j] as! CTRun
let runAttribute = CTRunGetAttributes(run) as NSDictionary
// if runAttribute == nil {return}
print("runCount == \(runs.count) j == \(j) runattribute == \(runAttribute)")
let runDelegate = runAttribute.object(forKey: kCTRunDelegateAttributeName as String)
if (runDelegate == nil) {continue}
var ascent:CGFloat = 0
var descent:CGFloat = 0
let width:CGFloat = CGFloat(CTRunGetTypographicBounds(run, CFRange(location: 0, length: 0), &ascent, &descent, nil))
let xoffset = lineOrigins[i].x + CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, nil)
let yoffset = lineOrigins[i].y
let drawRect = CGRect(x: xoffset, y: yoffset, width: width, height: ascent+descent)
if let imgName = runAttribute.object(forKey: "imageName") as? String {
let img = UIImage(named: imgName)
ctx.draw((img?.cgImage)!, in: drawRect)
}
}
}
}
关键代码是
a.获取lineOrigin CTFrameGetLineOrigins(frame, CFRange(location: 0, length: 0), &lineOrigins)
b.获取width,height CTRunGetTypographicBounds(run, CFRange(location: 0, length: 0), &ascent, &descent, nil) //图片忽略leading属性
c.获取x偏移量 lineOrigins[i].x+CTLineGetOffsetForStringIndex(line,CTRunGetStringRange(run).location,nil)
d.获取图片 runAttributes.object(forKey:"imageName")
coretext简单使用的更多相关文章
- CoreText 简单 使用
- (void)drawRect:(CGRect)rect { NSString *longText = @"CoreText"; /* ... */ NSRange rang = ...
- 图文混排--CoreText的简单运用
常见的在一些微博微信中可以看见一段文字中有不同的字体,字体有不同的颜色,并且可能会有一些笑脸之类的表情,这些可以通过图文混排做到. 图文混排可以通过WebView和CoreText做到,其他还有别的方 ...
- 简单的Coretext 图文混排
在很多新闻类或有文字展示的应用中现在都会出现图文混排的界面例如网易新闻等,乍一看去相似一个网页,其实这样效果并非由UIWebView 加载网页实现.现在分享一种比较简单的实现方式 iOS sdk中为我 ...
- 12.24笔记(关于//UIDynamic演练//多对象的附加行为//UIDynamic简单演练//UIDynamic//(CoreText框架)NSAttributedString)
12.24笔记1.UIDynamic注意点:演示代码:上面中设置视图旋转的时候,需要注意设置M_PI_4时,视图两边保持平衡状态,达不到仿真效果.需要偏移下角度.2.吸附行为3.推动行为初 ...
- iOS:基于CoreText的排版引擎
一.CoreText的简介 CoreText是用于处理文字和字体的底层技术.它直接和Core Graphics(又被称为Quartz)打交道.Quartz是一个2D图形渲染引擎,能够处理OSX和iOS ...
- 【iOS】使用CoreText实现图文混排
iOS没有现成的支持图文混排的控件,而要用多个基础控件组合拼成图文混排这样复杂的排版,是件很苦逼的事情.对此的解决方案有使用CoreText进行绘制,或者使用TextKit.本文主要讲解对于CoreT ...
- CoreText原理及基本使用方法
关于富文本的排版也是现在的一个技术点,以下是近日关于CoreText的学习记录以及个人理解,希望能对正在学习CoreText的朋友起到帮助. 1.框架坐标系 首先让我们先来看看CoreText坐标系和 ...
- iOS阅读器实践系列(一)coretext纯文本排版基础
前言:之前做了公司阅读类的App,最近有时间来写一下阅读部分的实现过程,供梳理逻辑,计划会写一个系列希望能涉及到尽量多的方面与细节,欢迎大家交流.吐槽.拍砖,共同进步. 阅读的排版用的是coretex ...
- 基于CoreText的基础排版引擎
storyboard: 新建一个CTDisplayView:UIView 代码如下: #import "CTDisplayView.h" #import "CoreTex ...
随机推荐
- flink反压的监控
反压在流式系统中是一种非常重要的机制,主要作用是当系统中下游算子的处理速度下降,导致数据处理速率低于数据接入的速率时,通过反向背压的方式让数据接入的速率下降,从而避免大量数据积压在flink系统中,最 ...
- Java核心技术卷阅读随笔--第3章【Java 的基本程序设计结构】
Java 的基本程序设计结构 现在, 假定已经成功地安装了 JDK,并且能够运行第 2 章中给出的示例程序.我们从现在开始将介绍 Java 应用程序设计.本章主要介绍程序设计的基本概念(如数据类型.分 ...
- VirtualBox安装配置CentOS7(含网络连接配置)
最近需要用到CentOS7,特地在虚拟机上安装一遍,中间走了很多弯路,特地在此处进行记录 前置条件: 1.本地完成Oracle VM VirtualBox,我安装的是6.1版本 2.下载CentOS安 ...
- python基础知识笔记(二) (出现语法以及颜色问题)
以下是在学习Python时需要尤其要注意的点: 1.程序中出现中文,运行的时候出现如下错误: SyntaxError: Non-UTF-8 code starting with '\xc1' 则是编码 ...
- springboot 非端口模式启动
@SpringBootApplication @ComponentScan(basePackages = {"demo.test"}) public class ReportApp ...
- Jmeter——元件扩展,使其功能更全面
工具扩展 在之前的博文中,有介绍自定义函数.Java请求扩展,博文如下: Jmeter二次开发--基于Java请求 Jmeter二次开发--自定义函数 上述内容,是按自己的需要来进行针对性扩展,从而实 ...
- Camera Calibration 相机标定
Camera Calibration 相机标定 一.相机标定方法 在opencv中提供了一组函数用于实现相机的标定,标定返回的值包括:相机内参矩阵(fx fy xc yc).相机外参矩阵(R t)以及 ...
- MindSpore基准性能
MindSpore基准性能 本文介绍MindSpore的基准性能.MindSpore网络定义可参考Model Zoo. 训练性能 ResNet 以上数据基于华为云AI开发平台ModelArts测试获得 ...
- Kubeedge Edged概述
Kubeedge Edged概述 Overview EdgeD是管理节点生命周期的边缘节点模块.它可以帮助用户在边缘节点上部署容器化的工作负载或应用程序.这些工作负载可以执行任何操作,从简单的远程遥测 ...
- HAL库与Cubemx系列|Systick-系统滴答定时器详解
Systick是什么? 关于Systick,在Context-M3权威指南中如此描述: SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号: 15).在以前,大多操作系统需要一 ...