Получить предыдущий объект CTFrame из заданной текстовой позиции с CoreText - PullRequest
1 голос
/ 30 марта 2020

Вот пример блока кода

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 начальная позиция?

Представьте, что у нас есть только текущая текстовая позиция и больше ничего. Также легко вычислить начальные позиции каждого кадра по отдельности от начала строки до конца и где-то кешировать их, но этот вопрос стал действительно сложным, если мы можем изменить размер шрифта атрибутивной строки или что-то подобное, что изменит целое CTFrame S. После изменения размера шрифта объект CTFrame должен быть нарисован из того же элемента currentTextPosition, который был до изменения размера шрифта, другими словами, изменение размера шрифта не должно влиять на текущую начальную позицию кадра.

1 Ответ

0 голосов
/ 18 апреля 2020

CTFramesetter не управляет списком CTFrame, а сами кадры являются неизменяемыми.

Ответственность за управление кадрами лежит на нас, поэтому, если есть какие-либо изменения в текстовом источнике, ie. Приписанная строка, мы должны заново сгенерировать все кадры с начала, потому что это единственный способ прогрессирования фрейм-установщика - нет режима обратного продвижения. К счастью, этот процесс оказался быстрым и может быть выполнен в фоновом потоке.

В том случае, если просто необходимо иметь рамку (например, закадровый рисунок и т. Д. c.) Для диапазона текста, предшествующего текущей позиции. (если, например, из текущей позиции нарисовано на экране), этот кадр может быть создан с использованием, например, следующего расширения

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)
    }
}

, конечно, оно не является обобщенным c, поскольку пути расположения могут отличаться, и атрибуты могут отличаться, но для упрощенных условий, предусмотренных в начальном вопросе topi c, его можно использовать.

...