我必须根据每行可以容纳的字符数将字符串拆分为数组。数组中的每个对象只需要一行文本。我能够计算字符串中的行数,但我无法弄清楚如何在一行中找出最大字符数。
func lineCount(forText text: String) -> Int {
let font = UIFont.systemFont(ofSize: 24.0)
let width: Int = Int(self.tableView.frame.size.width)
let rect: CGRect = text.boundingRect(with: CGSize(width: CGFloat(width), height: CGFloat(MAXFLOAT)), options: .usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil)
return Int(ceil(rect.size.height / font.lineHeight))
}
答案 0 :(得分:0)
由于某些字体的字符宽度可变,因此无法使用每种字体连续获取一定数量的字符。你可以做的是尝试将字符串分解为行大小的块:
extension String {
func split(width: CGFloat, font: UIFont) -> [String] {
guard !self.isEmpty else { return [String]() }
var lines = [String]()
// set up range of the split
var splitStart = self.startIndex
var splitEnd = self.startIndex
repeat {
// advance the end range for the split
splitEnd = self.index(after: splitStart)
// initial split to test
var line = String(characters[splitStart..<splitEnd])
// while we're before the end test the rendered width
while splitEnd < self.endIndex &&
line.size(attributes: [NSFontAttributeName: font]).width < width {
// add one more character
splitEnd = self.index(after: splitEnd)
line = String(characters[splitStart..<splitEnd])
}
// add split to array and set up next split
lines.append(line)
splitStart = splitEnd
} while splitEnd < self.endIndex // don't go past the end of the string
// add remainder of string to array
lines.append(String(characters[splitStart..<self.endIndex]))
return lines
}
}
这可以通过预先计算整个字符串的宽度来优化一点,除以行数,从每行的平均宽度开始,然后尝试更多或更少的字符直到它适合。但是,这确实使代码更加复杂。
如果你想确保单词不被分割,那么你可以保存每个单词开头的位置,当你到达一行的结尾时,将分词结束到单词之前,将单词带到下一次拆分。当然,您还需要考虑分割时间太长的单词,连字符等等。
另一种方法,因为您需要更高级的布局,可以使用NSLayoutManager
和NSTextContainer
:
let text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Non est ista, inquam, Piso, magna dissensio. Minime vero istorum quidem, inquit. Graecum enim hunc versum nostis omnes-: Suavis laborum est praeteritorum memoria. Negat enim summo bono afferre incrementum diem. Quasi ego id curem, quid ille aiat aut neget. Semper enim ex eo, quod maximas partes continet latissimeque funditur, tota res appellatur. Duo Reges: constructio interrete."
let font = UIFont.systemFont(ofSize: 24.0)
// set up styled text for the container
let storage = NSTextStorage(string: text, attributes: [NSFontAttributeName: font])
// add a layout manage for the storage
let layout = NSLayoutManager()
storage.addLayoutManager(layout)
// Set up the size of the container
// width is what we care about, height is maximum
let width:CGFloat = 500
let container = NSTextContainer(size: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude))
// add the container to the layout
layout.addTextContainer(container)
var lines = [String]()
// generate the layout and add each line to the array
layout.enumerateLineFragments(forGlyphRange: NSMakeRange(0, storage.length)) {
lines.append(storage.attributedSubstring(from: $0.3).string)
}
lines.forEach { print($0) }
结果:
Lorem ipsum dolor sit amet, consectetur
adipiscing elit. Non est ista, inquam, Piso,
magna dissensio. Minime vero istorum
quidem, inquit. Graecum enim hunc versum
nostis omnes-: Suavis laborum est
praeteritorum memoria. Negat enim summo
bono afferre incrementum diem. Quasi ego id
curem, quid ille aiat aut neget. Semper enim
ex eo, quod maximas partes continet
latissimeque funditur, tota res appellatur. Duo
Reges: constructio interrete.
如果您愿意,还可以通过NSLayoutManager
提供连字符和其他行为。
答案 1 :(得分:0)
迅速5 更新为最适合我的答案。
lineBreakMode
extension String {
func splittingLinesThatFitIn(width: CGFloat, font: UIFont) -> [String] {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byWordWrapping
// set up styled text for the container
let storage = NSTextStorage(string: self, attributes: [
NSAttributedString.Key.font: font,
NSAttributedString.Key.paragraphStyle: paragraphStyle
])
// add a layout manage for the storage
let layout = NSLayoutManager()
storage.addLayoutManager(layout)
// Set up the size of the container
// width is what we care about, height is maximum
let container = NSTextContainer(size: CGSize(width: width, height: CGFloat.greatestFiniteMagnitude))
// add the container to the layout
layout.addTextContainer(container)
var lines = [String]()
// generate the layout and add each line to the array
layout.enumerateLineFragments(forGlyphRange: NSMakeRange(0, storage.length)) { _, _, _, range, _ in
lines.append(storage.attributedSubstring(from: range).string)
}
debugPrint(lines)
return lines
}
}
答案 2 :(得分:-2)
我不确定这是否是最有效的方式,但我得到了它的工作:
func getLinesArrayOfString(forText text: String) ->NSArray {
let font = UIFont.systemFont(ofSize: 24.0)
let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.tableView.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
label.numberOfLines = 0
label.text = text as String
label.font = font
label.sizeToFit()
var linesArray: [Any] = []
let rect: CGRect = label.frame
let attStr = NSMutableAttributedString(string: text)
attStr.addAttribute((NSAttributedStringKey(rawValue: kCTFontAttributeName as String)), value: font, range: NSRange(location: 0, length: attStr.length))
let frameSetter: CTFramesetter = CTFramesetterCreateWithAttributedString(attStr)
let path: CGMutablePath = CGMutablePath()
path.addRect(CGRect(x: 0, y: 0, width: rect.size.width, height: 100000), transform: .identity)
let frame: CTFrame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, 0), path, nil)
let lines = CTFrameGetLines(frame) as? [Any]
for line: Any in lines! {
let lineRef = line
let lineRange: CFRange = CTLineGetStringRange(lineRef as! CTLine)
let range = NSRange(location: lineRange.location, length: lineRange.length)
let lineString: String = (text as NSString).substring(with: range)
CFAttributedStringSetAttribute(attStr, lineRange, kCTKernAttributeName, font)
CFAttributedStringSetAttribute(attStr, lineRange, kCTKernAttributeName, font)
linesArray.append(lineString)
}
return linesArray as NSArray
}