[Swift通天遁地]八、媒体与动画-(13)CoreText框架实现图文混排

本文将演示CoreText框架实现图文混排。CoreText(富文本)框架并不支持图片的绘制,

需要借助Core Graphics框架来进行图片的绘制。

图文混排的实现原理非常简单,就是在一个富文本中插入一个占位符,

表示此处需要插入一张图片。然后再由另一个图形绘制框架,

在占位符所在位置绘制指定的图片。

在项目文件夹上点击鼠标右键,弹出右键菜单。

【New File->【Cocoa Touch->【Next】->

【Class】:CTImageView

【Subclass of:UIView

【Language】:Swift

->Next->【Create】

点击打开【CTImageView.swift】,现在开始编写代码,创建一个包含多行文字的段落。 

  1 import UIKit
  2 
  3 //在类名的上方,添加两个浮点类型的全局变量,表示在富文本中插入的图片等尺寸。
  4 let picWidth = CGFloat(200.0)
  5 let picHeight = CGFloat(133.0)
  6 
  7 class CTImageView: UIView
  8 {
  9     //实现视图的绘制方法
 10     override func draw(_ rect: CGRect)
 11     {
 12         super.draw(rect)
 13         
 14         //设置填充颜色为橙色
 15         UIColor.orange.setFill()
 16         //在视图的显示区域填充橙色
 17         UIRectFill(rect)
 18         
 19         //获得图片占位符的尺寸信息,
 20         //该方法依次设置了,占位符的基线至占位符顶部的距离,
 21         //基线至占位符底部的距离,和占位符宽度三个尺寸的数据。
 22         var  ctRunCallback =  CTRunDelegateCallbacks(version: kCTRunDelegateVersion1,
 23                                                      dealloc:{ (refCon) -> Void in},
 24                                                      getAscent: { ( refCon) -> CGFloat in return picHeight},
 25                                                      getDescent: { (refCon) -> CGFloat in return 0
 26             }){ (refCon) -> CGFloat in
 27                 return picWidth
 28             }
 29         
 30         //初始化一个字符串变量,设置待插入的图片在项目文件夹中的名称。
 31         var picture = "coffee"
 32         //创建一个代理对象,作为占位符的代理属性。
 33         let ctRunDelegate  = CTRunDelegateCreate(&ctRunCallback, &picture)
 34         //创建一个可变属性的字符串对象,作为待插入图片的占位符,
 35         //它的内容就是一个简单的空格
 36         let placeHolder = NSMutableAttributedString(string: " ")
 37         
 38         //设置占位符属性的值,这样当绘制图片时,可以从该属性中,获得待绘制图片的位置和尺寸信息。
 39         placeHolder.addAttribute(kCTRunDelegateAttributeName as NSAttributedString.Key,
 40                                  value: ctRunDelegate!, range: NSMakeRange(0, 1))
 41         //继续给占位符添加一个自定义的属性,并设置属性的值为图片的名称,
 42         //这样当绘制图片时,可以从该属性中,获得待绘制图片的名称。
 43         placeHolder.addAttribute(NSAttributedString.Key(rawValue: "pictureName"),
 44                                  value: picture, range: NSMakeRange(0, 1))
 45         
 46         //创建一个字符串常量,表示富文本的内容。
 47         //图片将被插入到字符串的两个换行符之间的位置。
 48         let article = "Coffee is a brewed drink prepared from roasted coffee beans, which are the seeds of berries from the Coffea plant.\n\nThe genus Coffea is native to tropical Africa, and Madagascar, the Comoros, Mauritius and Réunion in the Indian Ocean."
 49         //通过一个字符串常量,创建一个富文本字符串。
 50         let attributedStr = NSMutableAttributedString(string: article)
 51         
 52         //将图片占位符插入到两个换行符之间的位置。
 53         attributedStr.insert(placeHolder, at: 115)
 54         //给富文本添加下划线样式,
 55         attributedStr.addAttribute(kCTUnderlineStyleAttributeName as NSAttributedString.Key,
 56                                    value: 1, range: NSRange(location: 0, length: attributedStr.length))
 57         
 58         //通过富文本对象,获得帧设置器,也就是帧的工厂类。
 59         let framesetter = CTFramesetterCreateWithAttributedString(attributedStr)
 60         //设置以当前的显示区域,作为绘制的区域。
 61         let path = UIBezierPath(rect: rect)
 62         //获得用于绘制的帧对象。
 63         let ctFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedStr.length), path.cgPath, nil)
 64         
 65         //在绘制之前,需要指定绘制的图形上下文,在此获得当前的图形上下文。
 66         let crtContext = UIGraphicsGetCurrentContext()
 67         //由于两个矿建的坐标系统的原点位置不同,所以需要对上下文进行一些变形操作,
 68         //首先设置图形上下文的字体矩阵。
 69         crtContext!.textMatrix = CGAffineTransform.identity
 70         //然后对图形上下文进行翻转。
 71         crtContext?.scaleBy(x: 1.0, y: -1.0)
 72         //再对图形上下文进行平移操作。
 73         crtContext?.translateBy(x: 0, y: self.bounds.size.height * -1)
 74         //使用帧绘制函数,将帧对象,绘制在指定的图形上下文中。
 75         CTFrameDraw(ctFrame, crtContext!)
 76         
 77         //此时虽然我们还没有在富文本中插入图片,但是富文本已经拥有了标志符,
 78         //所以在渲染时会自动保留指定的区域,等待图片的渲染。
 79         //本条语句用来从帧对象中,获得了所有的行对象。
 80         let ctLines = CTFrameGetLines(ctFrame) as NSArray
 81         //创建一个坐标点类型的数组,用来存储每一行文字原点的位置。
 82         var originsOfLines = [CGPoint]()
 83         //通过一个循环语句,
 84         for _ in 0..<ctLines.count
 85         {
 86             //将原点坐标存储在数组中。
 87             originsOfLines.append(CGPoint.zero)
 88         }
 89         
 90         //初始化一个范围对象
 91         let range: CFRange = CFRangeMake(0, 0)
 92         //设置每一行文字原点的位置
 93         CTFrameGetLineOrigins(ctFrame, range, &originsOfLines)
 94         
 95         //现在可以进行图片的绘制工作了,由于占位符处于CTRun对象中,
 96         //所以我们首先通过一个循环语句,对行数组,即6行的文字内容进行遍历。
 97         for i in 0..<ctLines.count
 98         {
 99             //获得当前行的原点位置
100             let ctLineOrigin = originsOfLines[i]
101             //获得当前行中的所有指定对象的数组
102             let ctRuns = CTLineGetGlyphRuns(ctLines[i] as! CTLine) as NSArray
103             
104             //通过一个循环语句,对数组进行遍历操作。
105             for ctRun in ctRuns
106             {
107                 //获得遍历到的对象的属性字典
108                 let ctAttributes = CTRunGetAttributes(ctRun as! CTRun) as NSDictionary
109                 //获得字典中的图片名称属性
110                 let pictureName = ctAttributes.object(forKey: "pictureName")
111                 //通过判断图片名称是否为空,来检测当前的对象,是否是插入的那个图片的占位符
112                 if pictureName != nil
113                 {
114                     //获得遍历到的对象,
115                     //在一行中的水平方向上的便宜距离
116                     let offset = CTLineGetOffsetForStringIndex(ctLines[i] as! CTLine, CTRunGetStringRange(ctRun as! CTRun).location, nil)
117                     //获得待绘制的图片,在水平方向上的位置
118                     let picturePosX = ctLineOrigin.x + offset
119                     //通过当前行的原点,水平偏移和图片的尺寸信息,
120                     //创建一个图片将被绘制的目标区域。
121                     let pictureFrame = CGRect(x: picturePosX, y: ctLineOrigin.y, width: picWidth, height: picHeight)
122                     //根据获得的图片名称属性,从项目文件夹中,读取指定名称的图片。
123                     let image = UIImage(named: pictureName as! String)
124                     //最后将图片绘制在指定占位区域。
125                     crtContext?.draw((image?.cgImage)!, in: pictureFrame)
126                 }
127             }
128         }
129     }
130 }

至此就完成了图片混排视图的所有编码工作。

在左侧的项目导航区,打开视图控制器的代码文件【ViewController.swift】

 1 import UIKit
 2 
 3 class ViewController: UIViewController {
 4 
 5     override func viewDidLoad() {
 6         super.viewDidLoad()
 7         // Do any additional setup after loading the view, typically from a nib.
 8         
 9         //在视图加载完成的方法中,使用上文刚刚创建的自定义视图。
10         //初始化一个自定义的视图对象。
11         let imageView = CTImageView()
12        //设置视图对象的显示区域
13         imageView.frame = CGRect(x: 0, y: 80, width: 320, height: 280)
14         
15         //设置根视图的背景颜色
16         self.view.backgroundColor = UIColor.black
17         //并将自定义视图添加到根视图
18         self.view.addSubview(imageView)
19     }
20 
21     override func didReceiveMemoryWarning() {
22         super.didReceiveMemoryWarning()
23         // Dispose of any resources that can be recreated.
24     }
25 }

猜你喜欢

转载自www.cnblogs.com/strengthen/p/10354875.html