Вертикальное центрирование текста в UITextView неправильной формы

Я работаю над рендерингом текста в центре круглой или овальной области. В идеале я хотел бы центрировать текст по вертикали и горизонтали, но чтобы он естественно обтекал границы области.

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

Подходы, которые я видел, предполагают настройку contentInset текстового представления после того, как текст был выложен и окончательная высота известна. (Например, этот вопрос) Однако в случае неправильной формы регион, высота текста после его размещения будет зависеть от того, с какой точки региона начинается макет.

Подход, который я рассмотрел, состоит в том, чтобы повторять процесс компоновки, пока не будет достигнута удовлетворительная компоновка. У кого-нибудь был успех с таким подходом? Одна проблема здесь заключается в том, что я не придумал, как определить, весь ли текст был отображен в представлении (т. е. достаточно ли места для размещения всего текста) --- есть ли способ запросить, весь ли контент был «рендеринг» при использовании UITextView?

Наконец: это только для отображения текста --- нет необходимости позволять пользователю редактировать содержимое представления. Будет ли CoreText лучшим подходом в этом случае?

Я новичок в iOS-разработке, поэтому, если я делаю что-то возмутительное, это тоже будет полезно знать!

Спасибо!

let boundingRect = CGRect(...)
let textView = UITextView(frame: boundingRect)

textView.editable = false
view.addSubview(textView)

let verticalInset:CGFloat = <some value>

let width = boundingRect.width
let height = boundingRect.height
let textBounds = CGSize(width: width, height:height - 2*verticalInset)
textView.bounds = textBounds

let exclusionRect = CGRect(x:0, y:-verticalInset, width:width, height:height)

let textRegion = UIBezierPath(ovalInRect: exclusionRect)    
let exclusionPath = UIBezierPath(rect:exclusionRect)
exclusionPath.appendPath(textRegion.bezierPathByReversingPath())

textView.textContainer.exclusionPaths = [exclusionPath]

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .Center

let attributes = [NSParagraphStyleAttributeName: paragraphStyle]
let formattedContent = NSAttributedString(string: "....", attributes:attributes)

textView.attributedText(formattedContent)

Вот изображение, которое поможет визуализировать вышеизложенное, если оно полезно: example


person duncanm    schedule 07.04.2016    source источник
comment
Это именно то, что я сейчас пытаюсь решить! Я думаю, что TextKit может быть единственным выходом.   -  person Christopher Kevin Howell    schedule 07.04.2016


Ответы (2)


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

Я считаю, что решение этого всегда итеративный метод. Например: визуализируйте текст в фигуре с увеличивающимися вставками и найдите самую большую вставку, чтобы текст помещался в результирующую фигуру.

Чтобы сделать это с основным текстом, вы можете создать CTFrame для текстового макета следующим образом:

let text:NSAttributedString = ... // The text to render
let path:CGPath = ... // Bounds the shape in which the text is rendered
let framesetter = CTFramesetterCreateWithAttributedString(text)
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)

С помощью этого frame вы можете проверить, была ли визуализирована вся строка:

let range = CTFrameGetVisibleStringRange(frame)
if text.length == range.length {
  // Fits
}

И отобразить текст:

let ctx:CGContext = ... 
CTFrameDraw(renderFrame, ctx)

Если вам нужна дополнительная информация об отображаемом тексте, вы можете использовать CTFrameGetLines, чтобы получить CTLine из frame. Затем, используя CTFrameGetLineOrigins и CTLineGetBoundsWithOptions с опцией UseGlyphPathBounds или UseOpticalBounds, вы можете рассчитать оптические границы для отдельных линий. Это обеспечивает более точную вертикальную регулировку. (Приготовьтесь к некоторой линейной алгебре, преобразования системы координат, необходимые для прямого использования CoreText с CoreGraphics, могут быть немного утомительными).

person Theo    schedule 12.05.2016

Я не могу предложить фантастический ответ здесь.

Однако я только что попробовал YYText. У него есть «YYLabel», который поддерживает вертикальное выравнивание текста и пути исключения. Взгляните на их демонстрационный проект, в частности на строку «TextAttributes1» в их табличном представлении, которая демонстрирует.

person Christopher Kevin Howell    schedule 07.04.2016
comment
Большое спасибо за предложение @Christopher. Я проверю это. На самом деле --- мне также удалось найти работающее решение для моего варианта использования - не очень красивое, но пока нормальное - которое я опубликую ниже. Ваше здоровье! - person duncanm; 08.04.2016
comment
К сожалению, следует добавить: как только у меня будет возможность проверить YYText, я отмечу ваш ответ. - person duncanm; 08.04.2016