Ios 使用CoreText从给定文本位置获取上一个CTFrame对象

Ios 使用CoreText从给定文本位置获取上一个CTFrame对象,ios,swift,core-text,ctframe,Ios,Swift,Core Text,Ctframe,下面是示例代码块 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 CFAttributed

下面是示例代码块

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
开始位置?


想象一下,我们只有当前文本位置,没有其他位置。从字符串的开头到结尾,单独计算每个帧的起始位置并将其缓存到某个位置也很容易,但是如果我们能够更改属性字符串字体大小或smth类似值,这将改变整个
CTFrame
,那么这个问题就变得非常棘手了。更改字体大小后,
CTFrame
对象应从更改字体大小前的相同currentTextPosition绘制,换句话说,更改字体大小不应影响当前帧的开始位置。
CTFramesetter
不管理
CTFrame
列表,框架本身是不可变的

框架管理由我们负责,因此,如果文本源(即属性字符串)中有任何更改,我们必须从一开始重新生成所有框架,因为这是框架设置程序进展的唯一方式-没有反向进展模式。幸运的是,这个过程被证明是快速的,并且可以在后台线程中完成

如果只需要在当前位置之前的文本范围内有一个框架(例如,屏幕外绘图等)(如果,例如,从当前位置在屏幕上绘制),可以使用以下扩展创建该框架

extension CTFramesetter {
    func prevFrame(position: Int) -> CTFrame {

        let size = CTFramesetterSuggestFrameSizeWithConstraints(self,
            CFRange(location: 0, length: position), nil,
            CGSize(width: UIScreen.main.bounds.width, height: CGFloat.greatestFiniteMagnitude), nil)

        let path = CGPath(rect: CGRect(origin: .zero, size: size), transform: nil)
        return CTFramesetterCreateFrame(self, CFRangeMake(0, position), path, nil)
    }
}

当然,它不是通用的,因为布局路径可以不同,属性可以不同,但对于主题开始问题中提供的简化条件,可以使用它。

CTFramesetter不管理
CTFrame
的列表,并且帧本身是不可变的

框架管理由我们负责,因此,如果文本源(即属性字符串)中有任何更改,我们必须从一开始重新生成所有框架,因为这是框架设置程序进展的唯一方式-没有反向进展模式。幸运的是,这个过程被证明是快速的,并且可以在后台线程中完成

如果只需要在当前位置之前的文本范围内有一个框架(例如,屏幕外绘图等)(如果,例如,从当前位置在屏幕上绘制),可以使用以下扩展创建该框架

extension CTFramesetter {
    func prevFrame(position: Int) -> CTFrame {

        let size = CTFramesetterSuggestFrameSizeWithConstraints(self,
            CFRange(location: 0, length: position), nil,
            CGSize(width: UIScreen.main.bounds.width, height: CGFloat.greatestFiniteMagnitude), nil)

        let path = CGPath(rect: CGRect(origin: .zero, size: size), transform: nil)
        return CTFramesetterCreateFrame(self, CFRangeMake(0, position), path, nil)
    }
}
当然,它不是通用的,因为布局路径可以不同,属性也可以不同,但对于主题开始问题中提供的简化条件,可以使用它