Как разместить текст в обратном порядке, используя основной текст?

Для размещения длинной строки на страницах я могу использовать CTFramesetterCreateFrame для создания CTFrameRef, а затем перемещать начальный индекс в строке для создания следующего CTFrameRef. Это легко, поскольку начальный индекс продвигается вперед, а API-интерфейсы основного текста изначально поддерживают такой макет.

Теперь я застрял в ситуации, когда хочу вернуться на предыдущую страницу.

Например, длина длинной строки равна 1000, теперь начальный индекс текущей страницы (или CTFrameRef) равен 500, и я хочу перейти на предыдущую страницу, как мне узнать, с какого индекса начинать? Я не могу кэшировать все индексы для каждой страницы или вычислять с начала строки, потому что строка может быть очень длинной и не помещаться в памяти (мне нужно будет читать строку из файла поблочно) . Я просмотрел CT* API, нет такого API, который поддерживает размещение текста в обратном порядке.

Любые идеи по этому поводу?


person neevek    schedule 10.03.2012    source источник


Ответы (1)


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

Если каждая страница вашего текста имеет заданную ширину и представляет собой просто прямоугольный CTFrame, вот как вы можете это сделать:

  1. «Оценить» диапазон строк для предыдущей страницы (например, предположим, что он имеет ту же длину, что и текущая страница.
  2. Превышение этой оценки на разумно большой коэффициент (например, в 1,5/2 раза больше длины) до разумного. Выберите диапазон промаха, чтобы он начинался в начале абзаца.
  3. Создайте рамку с этим диапазоном, как обычно, но сделайте рамку намного больше, чем обычно.
  4. Получите информацию о строке из кадра и с конца начните считать высоту, возвращаясь построчно.
  5. Вы можете найти фактическую предыдущую страницу, начинающуюся с индекса первой подходящей строки, и использовать ее для создания фактического CTFrame.

Хотя это должно работать, я все же рекомендую вам попытаться найти способ избежать этого, если это возможно. Обратите внимание, что это ломается, как только у вас есть более сложный алгоритм разбиения на страницы, и даже в этом случае он использует память в 1,5/2 раза больше. Вот несколько советов о том, как вы можете это сделать:

  1. Кэшируйте индексы (по крайней мере, до текущей позиции). Каждая страница, вероятно, может содержать ~ 1000 символов, поэтому размер вашего кеша будет составлять 0,1% от размера всего текста, и вы должны сделать это хотя бы один раз в первый раз. вы показываете текст. Вы можете сделать кеш еще меньше, сохраняя индексы для каждой n страницы.

  2. Если это применимо, разбейте строку на логические подстроки, например, на основе разрыва страницы или разрыва раздела, а затем напечатайте каждую подстроку в целом для диапазона страниц.

person mohsenr    schedule 15.03.2012
comment
Спасибо @mohsenr, мой вопрос наконец-то получил ответ. Я уже пробовал метод на 99% такой же, как ваше первое предложение. Вместо того, чтобы оценивать диапазон строк для предыдущей страницы в 1,5/2 раза больше длины текущей страницы, я предположил, что страница будет содержать только один символ a несколько раз (я думаю, что это достаточно мало), если страница содержит 1000 a's, я отступит на 1000 символов, чтобы создать CTFrame и сделать то, что вы предложили в пунктах 4 и 5. - person neevek; 15.03.2012
comment
Второе ваше предложение не лучше первого. Любой кеш сломается при изменении размера текста или шрифта, и будет дорого перестраивать индекс. Я могу кешировать индикаторы для n страниц при перелистывании следующих страниц, но я не всегда могу зависеть от этого кеша. Во всяком случае, ваш ответ подтвердил мне, что в iOS нет эквивалента Android Paint.breakText().. - person neevek; 15.03.2012