1. 程式人生 > >[Swift通天遁地]八、媒體與動畫-(13)CoreText框架實現圖文混排

[Swift通天遁地]八、媒體與動畫-(13)CoreText框架實現圖文混排

rst context 工廠 控制 制圖 sge getattr cgpath origin

本文將演示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 }

[Swift通天遁地]八、媒體與動畫-(13)CoreText框架實現圖文混排