这里是示例代码块
let attrString = NSAttributedString(string: "very long string goes here...") // very long string
var currentTextPos = 400 // current text position. ex. 400
let framesetter = CTFramesetterCreateWithAttributedString(attrString as CFAttributedString)
let path = CGPath(rect: UIScreen.main.bounds, transform: nil)
let ctFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(currentTextPos, 0), path, nil)
// draw frame with CTFrameDraw() function later
作为示例,假设我们有一个很长的属性字符串,并且我们希望从400索引开始绘制一个框架。
要绘制下一帧,我们可以计算出当前帧中可以容纳多少个字符并进行绘制。这是一个如何实现的示例:
// Get the length of string which fits in current CTFrame object
let frameLength = CTFrameGetVisibleStringRange(ctFrame).length // ex. frameLength == 95
// Append this to our currentTextPos variable
currentTextPos += frameLength // 400 + 95 = 495 . This is our next frame starting position
// Create another frame starting from 495 position
let nextFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(currentTextPos, 0), path, nil)
在这种情况下,我们可以获取下一个CTFrame
对象。这很容易实现,因为CTFramesetterCreateFrame
将用一行填充整个帧,直到到达帧末尾或直到字符串末尾为止。因此,很容易获得下一个CTFrame
起始位置。
主要问题是如何获取先前的CTFrame
对象?还是先前的CTFrame
起始位置?
想象一下,我们只有当前的文字位置,没有别的。从字符串的开头到结尾分别计算每个帧的开始位置并将其缓存在某个位置也很容易,但是如果我们能够更改属性字符串的字体大小或类似的值,从而改变整个[C0 ]的。更改字体大小后,应从更改字体大小之前的currentTextPosition中绘制CTFrame
对象,换句话说,更改字体大小不应影响当前帧的开始位置。
CTFrame
不管理CTFramesetter
的列表,并且帧本身是不可变的。
帧管理是我们的责任,因此,如果文本源发生任何更改,即归因于字符串,我们必须从开始就re-generate所有帧,因为这是框架设定器前进的唯一途径-没有反向前进模式。幸运的是,该过程被证明很快,并且可以在后台线程中完成。