美文网首页工作生活
富文本 CoreText 和 TextKit

富文本 CoreText 和 TextKit

作者: 小凡凡520 | 来源:发表于2020-02-11 09:10 被阅读0次
    一、CoreText

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

    二、说明
    1、CTFrame可以想象成画布, 画布的大小范围由CGPath决定
    2、CTFrame由很多CTLine组成, CTLine表示为一行
    3、CTLine由多个CTRun组成, CTRun相当于一行中的多个块, 但是CTRun不需要你自己创建, 由NSAttributedString的属性决定, 系统自动生成。每个CTRun对应不同属性。
    4、CTFramesetter是一个工厂, 创建CTFrame, 一个界面上可以有多个CTFrame
    5、CTFrame就是一个基本画布,然后一行一行绘制。 CoreText会自动根据传入的NSAttributedString属性创建CTRun,包括字体样式,颜色,间距等
    
    1642760-d88061e35bc6ba1d.png
    三、使用

    CoreText是需要自己处理绘制,不像UILabel等最上层的控件 ,所以我们必须在drawRect中绘制,为了更好地使用,我们稍微封装一下,自定义一个UIView。

    我们在使用上层的控件时,坐标系的原点在左上角,而底层的Core Graphics的坐标系原点则是在左下角,以下是一个最基本的绘制示例:

    class Myview: UIView {
        
        override func draw(_ rect: CGRect) {
            super.draw(rect)
            
            //step 1:获取当前画布的上下文
            if let context = UIGraphicsGetCurrentContext() {
                //step 2:
                let path = CGMutablePath.init()
                path.addRect(self.bounds)
                //step 3:
                let attributedString = NSMutableAttributedString(string: "单身公寓")
                //step 4:
                let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
                let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedString.length), path, nil)
                //step 5:
                // CTFrameDraw
                CTFrameDraw(frame, context)
    
                // 按CTLine绘制
                // 1.获得CTLine数组
                let lines = CTFrameGetLines(frame)
                // 2.获得行数
                let numberOfLines = CFArrayGetCount(lines)
                // 3.获得每一行的origin, CoreText的origin是在字形的baseLine处的, 请参考字形图
                var lineOrigins = [CGPoint](repeating: CGPoint.zero, count: numberOfLines)
                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), to: CTLine.self)
                    // 设置每一行的位置
                    context.textPosition = origin
                    // 开始一行的绘制
                    CTLineDraw(line, context)
                }
    
                // 1.获得CTLine数组
                let lines = CTFrameGetLines(frame)
                // 2.获得行数
                let numberOfLines = CFArrayGetCount(lines)
                // 4.遍历每一行进行绘制
                for index in 0..<numberOfLines {
                    // 参考: http://swifter.tips/unsafe/
                    let line = unsafeBitCast(CFArrayGetValueAtIndex(lines, index), to: CTLine.self)
                    drawLine(line: line, context: context)
                }
            }
        }
    
        // 按CTRun绘制
        func drawLine(line: CTLine, context: CGContext) {
            let runs = CTLineGetGlyphRuns(line) as Array
            runs.forEach { run in
                CTRunDraw(run as! CTRun, context, CFRangeMake(0, 0))
            }
        }
    }
    
    1642760-a3b04adcefbddc3c.png

    结果分析:发现文案是反的。原因就是因为coreText的坐标系是和UIKit的坐标系不一样的:

    1642760-0d735191bf8d9143.jpeg

    如上图,CoreText是基于CoreGraphics的,所以坐标系原点是左下角,我们需要进行翻转。将Y轴从向上转换为向下。

    context.textMatrix = CGAffineTransform.identity
    context.translateBy(x: 0, y: self.bounds.size.height)
    context.scaleBy(x: 1.0, y: -1.0)
    
    1642760-688ae81a11ed68b8.png
    四、图文混排

    CoreText本身是不提供UIImage的绘制,所以UIImage肯定只能通过Core Graphics绘制,但是绘制时双必须要知道此绘制单元的长宽,庆幸的是CoreText绘制的最小单元CTRun提供了CTRunDelegate,也就是当设置了kCTRunDelegateAttributeName过后,CTRun的绘制时所需的参考(长宽等)将可从委托中获取,我们即可通过此方法实现图片的绘制。在需要绘制图片的位置,提前预留空白占位。
    CTRun有几个委托用以实现CTRun的几个参数的获取。

    文字的绘制只需要知道文字的大小就够了,而图片的绘制不一样,需要知道图片的坐标,高度和宽度。在CoreText中,我们可以把插入的图片当做一个特殊的CTRun,通过delegate来设置图片的宽度和高度,这样就解决了图片的高度和宽度问题,但是CoreText不会自动的对图片进行绘制,因此需要我们自己找到图片的显示位置(原点坐标),然后自己进行绘制

    五、图片点击事件

    CoreText就是将内容绘制到画布上,自然没有事件处理,我们要实现图片与链接的点击效果就需要使用触摸事件了。当点击的位置在图片的Rect中,那我们做相应的操作即可,所以基本步骤如下:

    记录所有图片所在画布中作为一个CTRun的位置 -> 获取每个图片所在画布中所占的Rect矩形区域 -> 当点击事件发生时,判断点击的点是否在某个需要处理的图片Rect内。
    
    六、链接点击事件
    七、AzzttribuedString

    AttribuedString对象包含很多的属性,每一个属性都有其对应的字符区域,使用NSRange来进行描述的。

    1、用NSDictionary来存放属性值
    2、对range内的字符指定属性
    3、对于控件上字符都是通过NSTextStorage实例来存储
    4、isEqual比较每个字符以及他们的属性来判等
    5、和CFAttributedStringRef无缝桥接
    

    相关文章

      网友评论

        本文标题:富文本 CoreText 和 TextKit

        本文链接:https://www.haomeiwen.com/subject/vischctx.html