避免多行 UILabel 中的单个字符换行
Avoid single characters in multiline UILabel for newline
我有一个 numberOfLines = 2 的(动态大小的)UILabel。
取决于来自 BE 的文字,文字是否包裹得很好。
有没有人有任何关于如何防止(长)词分成两行并且在新行中只有一个字符的提示?
当前行为:
Thisisalongwor
d
通缉行为:
Thisisalong
word
基本上:我想设置每行的最少字符数(当将项目从第一行换行到第二行时)。
谢谢!
这是一种方法...
使用 CoreText 函数从标签中获取 wrapped-lines 的数组。如果最后一行有至少1个字符,但少于4个字符,且全文大于4个字符,从末尾插入一个line-feed4个字符文本并更新标签。
因此,基于默认 UILabel
- 17 磅系统字体 - fixed-width 为 123-pts
且换行设置为 Character Wrap
,它看起来像这样:
在 运行 之后 fixLabelWrap(...)
函数看起来像这样:
示例代码:
class CharWrapViewController: UIViewController {
@IBOutlet var theLabel: UILabel!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
fixLabelWrap(theLabel)
}
func fixLabelWrap(_ label: UILabel) -> Void {
// get the text from the label
var text = theLabel.text ?? ""
// get an array of the char-wrapped text
let lines = getLinesArrayOfString(in: theLabel)
// if it is more than one line
if lines.count > 1 {
// get the last line
if let lastLine = lines.last {
// if the last line has at least 1 char, is less than 4 chars, and
// the full text is greater than 4 chars
if lastLine.count > 0 && lastLine.count < 4 && text.count > 4 {
// insert a line-feed 4 chars before the end
text.insert("\n", at: text.index(text.endIndex, offsetBy: -4))
// update the text in the label
theLabel.text = text
}
}
}
}
func getLinesArrayOfString(in label: UILabel) -> [String] {
/// An empty string's array
var linesArray = [String]()
guard let text = label.text, let attStr = label.attributedText else { return linesArray }
let rect = label.frame
let frameSetter: CTFramesetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
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)
guard let lines = CTFrameGetLines(frame) as? [Any] else {return linesArray}
for line in lines {
let lineRef = line as! CTLine
let lineRange: CFRange = CTLineGetStringRange(lineRef)
let range = NSRange(location: lineRange.location, length: lineRange.length)
let lineString: String = (text as NSString).substring(with: range)
linesArray.append(lineString)
}
return linesArray
}
}
注意:getLinesArrayOfString(...)
函数是 post 的略微修改版本,可在此处找到:
我有一个 numberOfLines = 2 的(动态大小的)UILabel。
取决于来自 BE 的文字,文字是否包裹得很好。
有没有人有任何关于如何防止(长)词分成两行并且在新行中只有一个字符的提示?
当前行为:
Thisisalongwor
d
通缉行为:
Thisisalong
word
基本上:我想设置每行的最少字符数(当将项目从第一行换行到第二行时)。
谢谢!
这是一种方法...
使用 CoreText 函数从标签中获取 wrapped-lines 的数组。如果最后一行有至少1个字符,但少于4个字符,且全文大于4个字符,从末尾插入一个line-feed4个字符文本并更新标签。
因此,基于默认 UILabel
- 17 磅系统字体 - fixed-width 为 123-pts
且换行设置为 Character Wrap
,它看起来像这样:
在 运行 之后 fixLabelWrap(...)
函数看起来像这样:
示例代码:
class CharWrapViewController: UIViewController {
@IBOutlet var theLabel: UILabel!
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
fixLabelWrap(theLabel)
}
func fixLabelWrap(_ label: UILabel) -> Void {
// get the text from the label
var text = theLabel.text ?? ""
// get an array of the char-wrapped text
let lines = getLinesArrayOfString(in: theLabel)
// if it is more than one line
if lines.count > 1 {
// get the last line
if let lastLine = lines.last {
// if the last line has at least 1 char, is less than 4 chars, and
// the full text is greater than 4 chars
if lastLine.count > 0 && lastLine.count < 4 && text.count > 4 {
// insert a line-feed 4 chars before the end
text.insert("\n", at: text.index(text.endIndex, offsetBy: -4))
// update the text in the label
theLabel.text = text
}
}
}
}
func getLinesArrayOfString(in label: UILabel) -> [String] {
/// An empty string's array
var linesArray = [String]()
guard let text = label.text, let attStr = label.attributedText else { return linesArray }
let rect = label.frame
let frameSetter: CTFramesetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
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)
guard let lines = CTFrameGetLines(frame) as? [Any] else {return linesArray}
for line in lines {
let lineRef = line as! CTLine
let lineRange: CFRange = CTLineGetStringRange(lineRef)
let range = NSRange(location: lineRange.location, length: lineRange.length)
let lineString: String = (text as NSString).substring(with: range)
linesArray.append(lineString)
}
return linesArray
}
}
注意:getLinesArrayOfString(...)
函数是 post 的略微修改版本,可在此处找到: