即使有足够的空间可用于单词,UILabel 自动换行功能也会留下空间

Posted

技术标签:

【中文标题】即使有足够的空间可用于单词,UILabel 自动换行功能也会留下空间【英文标题】:UILabel word wrap feature leaving space even when sufficient space available for the word 【发布时间】:2021-04-11 04:07:34 【问题描述】:

这个问题甚至出现在故事​​板中。

UILabel 具有以下属性:

numberOfLines = 0 lineBreakMode = .byWordWrapping 约束:领先和落后 26 点到 superview;垂直居中。 自定义字体:中等 17 磅。

如您所见,第四个单词无法放入第一行,因此会出现布局错误的问题。如果我删除最后一个单词,则句子完全适合一行或说出第四个单词。如果在它之后添加一个单词会将它们都移动到下一行,这会留下很多空间。它应该尝试在一行中尽可能不中断或连字符来适应单词。但是很明显,即使单词可以容纳,也会产生空白。

您可以在新项目中重新创建它并观察问题。

【问题讨论】:

使用label.sizeToFit()。它可能会有所帮助。 还是没有帮助... Apple 设计了自动换行算法来避免“寡妇”——即避免将单个单词作为段落的最后一行。在谷歌上快速搜索UILabel widows,你会发现很多讨论解释了它发生的原因,以及尝试解决它的各种方法。 嗨@DonMag 感谢您为我指明正确的方向。但是它与自动换行没有任何关系,因为它在所有换行模式下都表现出相同的行为。 如果你将它设置为Character Wrap,它的行为会有所不同,但当然这会在单词中间中断,我确定你不想要。 Apple 在 ios 10 或 11 左右改变了这一点(我记得)。如果它适用于您的目的,您可以使用不可滚动、不可编辑的 UITextView 代替您的 UILabel -- 文本视图避免寡妇和孤儿(我应该说“孤儿”开头,有区别)。 【参考方案1】:

您可能想尝试一下...

子类UITextView,禁用滚动、编辑和选择...将textContainerInset = UIEdgeInsets.zerotextContainer.lineFragmentPadding = 0 设置为零。

结果:

代码(@IBDesignable 所以我们可以在 IB / Storyboard 中看到它):

@IBDesignable
class TextViewLabel: UITextView 

    override init(frame: CGRect, textContainer: NSTextContainer?) 
        super.init(frame: frame, textContainer: textContainer)
        commonInit()
    

    required init?(coder aDecoder: NSCoder) 
        super.init(coder: aDecoder)
        commonInit()
    

    func commonInit() -> Void 
        isScrollEnabled = false
        isEditable = false
        isSelectable = false
        textContainerInset = UIEdgeInsets.zero
        textContainer.lineFragmentPadding = 0
    


【讨论】:

非常感谢!! :) @kelalaka - 哎呀......已修复。【参考方案2】:

此代码可用于固定宽度的标签,它会克服这一点, 也可以得到标签的高度

  private static func calculateLabelProperties() -> (String, CGFloat)

    let titleString = "Your string here"

        let wordArray = titleString.components(separatedBy: " ")
        var lineCount = 1//first line denoted by 1
        var tempWidth:CGFloat = 0
        var title = ""
        for (index,element) in wordArray.enumerated() 
            let width = calculateFontSize(title: element + " ", fontName: "your font name", fontSize: 17).width
            tempWidth = tempWidth + width

            //get the nextelement
            var nextElement = ""
            if wordArray.count > index + 1 
                nextElement = wordArray[index + 1]
            

            let nextElementWidth = calculateFontSize(title: nextElement, fontName: "your font name", fontSize: 17).width

            if tempWidth + nextElementWidth > 410 //410 is labelwidth
                tempWidth = 0
                lineCount += 1
                title = "\(title)\(element)\n"
             else 
                title = "\(title)\(element) "
            
        
        return (title, CGFloat(lineCount * 20))//20 is label sigle line height
    

此代码计算字体大小

func calculateFontSize(title: String, fontName: String, fontSize: CGFloat) -> (width: CGFloat, height: CGFloat)
    let font = UIFont(name: fontName, size: fontSize)
    return (title.size(OfFont: font!).width, title.size(OfFont: font!).height)

获取字体大小

extension String 
    func size(OfFont font: UIFont) -> CGSize 
        return (self as NSString).size(withAttributes: [NSAttributedString.Key.font: font])
    

【讨论】:

以上是关于即使有足够的空间可用于单词,UILabel 自动换行功能也会留下空间的主要内容,如果未能解决你的问题,请参考以下文章

多行 UILabel 没有正确换行 [重复]

ios自动布局标签定位

如何在 iOS 的 UILabel/UITextView 中处理多种屏幕尺寸的中文自动换行?

UILabel 自动换行/字符换行

无法在具有动态宽度的 Tablecell 中并排自动布局两个 UILabel

UILabel 有时不能正确换行(自动布局)