Очень важное изменение в отношении iOS 11 +
Начиная с iOS 11, Apple преднамеренно изменила поведение своей функции переноса слов для UILabel
, которая влияетобнаружение String
содержимого отдельных строк в многострочном UILabel
.По замыслу перенос слов UILabel
теперь позволяет избежать потерянного текста (отдельных слов в новой строке), как обсуждалось здесь: перенос слов в iOS 11
Из-за этогоспособ, которым CTFrameGetLines(frame)
возвращает массив CTLine
всех строк в метке, больше не работает должным образом, если в определенной строке вступает в силу новая переносимость слов, которая позволяет избежать потерянного текста.Наоборот, это приводит к тому, что в некоторых частях String
новый дизайн переноса слов будет принадлежать следующей строке, а не попадать в фокус в строке.
Можно найти проверенное исправление для этой проблемы.в моей измененной версии @ TheTiger's answer , в которой используется расчет фактического размера содержимого UILabel
с использованием sizeThatFits(size:)
, перед использованием этого размера для создания прямоугольника / пути, записанного в Swift4 :
extension UILabel {
/// creates an array containing one entry for each line of text the label has
var lines: [String]? {
guard let text = text, let font = font else { return nil }
let attStr = NSMutableAttributedString(string: text)
attStr.addAttribute(NSAttributedString.Key.font, value: font, range: NSRange(location: 0, length: attStr.length))
let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
let path = CGMutablePath()
// size needs to be adjusted, because frame might change because of intelligent word wrapping of iOS
let size = sizeThatFits(CGSize(width: self.frame.width, height: .greatestFiniteMagnitude))
path.addRect(CGRect(x: 0, y: 0, width: size.width, height: size.height), transform: .identity)
let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attStr.length), path, nil)
guard let lines = CTFrameGetLines(frame) as? [Any] else { return nil }
var linesArray: [String] = []
for line in lines {
let lineRef = line as! CTLine
let lineRange = CTLineGetStringRange(lineRef)
let range = NSRange(location: lineRange.location, length: lineRange.length)
let lineString = (text as NSString).substring(with: range)
linesArray.append(lineString)
}
return linesArray
}
}
Это расширение UILabel
возвращает содержимое метки в виде массива String
с одной записью на строку в точности так, как это представлено глазу пользователя.