带有段落样式行间距的 UILabel

Posted

技术标签:

【中文标题】带有段落样式行间距的 UILabel【英文标题】:UILabel with Paragraph Style Line Spacing 【发布时间】:2015-08-26 01:41:57 【问题描述】:

我正在尝试创建一个消息传递应用程序,但遇到了一个非常奇怪的问题。

“Thomas”和文本气泡底部之间有这么多空间的原因是因为UILabel 正在创建另一行。目前我正在使用attributedText 属性设置标签的文本,并传入NSMutableParagraphStyleline spacing8。如果我将line spacing 设置为0,“Thomas”和文本气泡底部之间的空间会像这样消失:

这就是它变得奇怪的地方。如果我将段落line spacing 设置回8,并在该行中添加更多字符,则会出现没有多余行的文本气泡:

非常感谢所有帮助:)

这是我的代码:

class MessageTableViewCell: UITableViewCell 
    var didSetupConstraints = false

    var thumbnail = UIImageView.newAutoLayoutView()
    let messageTailIcon = UIImageView.newAutoLayoutView()
    var messageView = UIView.newAutoLayoutView()
    var messageLabel = UILabel.newAutoLayoutView()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupViews()
    

    required init(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

    func setupViews() 
        thumbnail.image = UIImage(named: "ThomasBaldwin")
        thumbnail.layer.cornerRadius = 17.5
        thumbnail.clipsToBounds = true

        messageTailIcon.image = UIImage(named: "MessageTailIcon")

        messageView.backgroundColor = Application.greyColor
        messageView.layer.cornerRadius = 10

        let paragraphStyle = NSMutableParagraphStyle.new()
        paragraphStyle.lineSpacing = 8

        messageLabel.numberOfLines = 0
        messageLabel.layer.cornerRadius = 10
        messageLabel.attributedText = NSMutableAttributedString(
            string: "Thomas says hello",
            attributes: [
                NSFontAttributeName: UIFont(name: "AvenirNextLTPro-Regular", size: 12.5)!,
                NSForegroundColorAttributeName: UIColor.colorFromCode(0x262626),
                NSBackgroundColorAttributeName: Application.greyColor,
                NSKernAttributeName: 0.5,
                NSParagraphStyleAttributeName: paragraphStyle
            ]
        )

        contentView.addSubview(thumbnail)
        contentView.addSubview(messageView)
        messageView.addSubview(messageTailIcon)
        messageView.addSubview(messageLabel)
        updateConstraints()
    

    override func updateConstraints() 
        if !didSetupConstraints 

            thumbnail.autoPinEdgeToSuperviewEdge(.Top, withInset: 15)
            thumbnail.autoPinEdgeToSuperviewEdge(.Leading, withInset: 8.5)
            thumbnail.autoSetDimensionsToSize(CGSize(width: 35, height: 35))

            messageView.autoPinEdgeToSuperviewEdge(.Top, withInset: 17.5)
            messageView.autoPinEdge(.Leading, toEdge: .Trailing, ofView: thumbnail, withOffset: 10)
            messageView.autoPinEdgeToSuperviewEdge(.Trailing, withInset: 24.5)
            messageView.autoPinEdgeToSuperviewEdge(.Bottom)

            messageTailIcon.autoPinEdgeToSuperviewEdge(.Top, withInset: 15)
            messageTailIcon.autoPinEdgeToSuperviewEdge(.Leading, withInset: -10)
            messageTailIcon.autoSetDimensionsToSize(CGSize(width: 18, height: 9))

            messageLabel.autoPinEdgesToSuperviewEdgesWithInsets(UIEdgeInsets(top: 8.5, left: 10, bottom: 8.5, right: 5), excludingEdge: .Trailing)
            messageLabel.autoPinEdgeToSuperviewEdge(.Trailing, withInset: 0, relation: .GreaterThanOrEqual)

            didSetupConstraints = true
        

        super.updateConstraints()
    


如果您想查看演示该问题的示例项目,我已将其推送至github

【问题讨论】:

我在想是因为单元格被重用了,所以可能是由其他项目(单元格)属性引起的。你只有一个细胞吗?或表格视图中的多个单元格?可以分享大一点的屏幕吗? @thomas,我认为您的 UIEdgeInsets 可能会导致您出现问题。由于您使用的自动布局库,很难告诉您可能是什么问题。很难发现这是您的代码有问题还是布局库的错误。 @Larcerax 我将一个演示该问题的示例项目推送到了 github,因此你们可以看到完整的代码:github.com/thomasbaldwin/SampleMessage @Vigor 我将一个演示该问题的示例项目推送到 github,以便你们可以看到完整的代码 谢谢,让我看看这个,我只以编程方式做,看起来你也在做同样的事情,如果我看到任何东西,我会回复 【参考方案1】:

好的,所以终于锁定了简单的答案,这仅与您的 KERNING 属性有关。看这个:

也忽略红细胞的大小,这在应用程序中不会发生这种情况,这只是我的屏幕截图大小不同的产物,但请自己尝试一下。注释掉字距并重新应用它,你会看到同样的东西

使用“Thomas”字距调整

不使用“Thomas”进行字距调整

带有“Thomas says hello”的字距调整

不使用“Thomas say hello”进行字距调整

我已经尽一切可能检查代码,使用不同的约束,我什至玩弄了 NSAttributedString 的所有选项,唯一改变这种不良行为的是字距调整属性,它对所有人都这样做字体类型,而不仅仅是 Avenir。事实上,你在这个例子中使用的字体是系统字体,当你根本没有设置字体时,但我现在用 3 种字体尝试过,同样的效果,字距似乎被破坏了,或者它可能按预期工作对于 Swift 和/或 ObjC,但我认为这实际上是一个错误。

大多数 NSAttributedString 选项,如果你想弄乱一些东西:

    var myString1 = NSMutableAttributedString(string:"Thomas asdfadsf asdfasdfasdf asdfasdf asdfasdf \n asdfasdf asdf \n")

    let myString1Font1 = UIFont.systemFontOfSize(12.0)

    let originalNSString = myString1.string as NSString
    let myString1Range1 = originalNSString.rangeOfString(myString1.string)

    var myString1ParaStyle1 = NSMutableParagraphStyle()
    myString1ParaStyle1.alignment = NSTextAlignment.Natural
    myString1ParaStyle1.baseWritingDirection = NSWritingDirection.Natural
    myString1ParaStyle1.defaultTabInterval = 0.0
    myString1ParaStyle1.firstLineHeadIndent = 0.0
    myString1ParaStyle1.headIndent = 0.0
    myString1ParaStyle1.hyphenationFactor = 0.0
    myString1ParaStyle1.lineBreakMode = NSLineBreakMode.ByWordWrapping
    myString1ParaStyle1.lineHeightMultiple = 0.0
    myString1ParaStyle1.lineSpacing = 8.0
    myString1ParaStyle1.maximumLineHeight = 0.0
    myString1ParaStyle1.minimumLineHeight = 0.0
    myString1ParaStyle1.paragraphSpacing = 0.0
    myString1ParaStyle1.paragraphSpacingBefore = 0.0
    myString1ParaStyle1.tailIndent = 0.0

    myString1.addAttribute(NSKernAttributeName, value:0.5, range:myString1Range1)
    myString1.addAttribute(NSFontAttributeName, value:myString1Font1, range:myString1Range1)
    myString1.addAttribute(NSParagraphStyleAttributeName, value:myString1ParaStyle1, range:myString1Range1)
    myString1.addAttribute(NSBackgroundColorAttributeName, value:UIColor.redColor(), range:myString1Range1)
    myString1.addAttribute(NSForegroundColorAttributeName, value:UIColor.blackColor(), range:myString1Range1)

再说一遍,这不是限制问题,我错了,这只是 KERNING 问题,这很糟糕,但生活就是这样,也许这需要向 RADAR 报告。

另外,您可以自己尝试一下,除了 0 或 0.00000 或您想要的任意数量的零之外的任何内容都会产生错误的字距调整结果,我试过这个,它会弄乱您的标签字段,就像字距会弄乱一样使用较大值的字段:

 NSKernAttributeName: 0.00000000000001

等一下,我解决了,从它的样子,设置这个值,只需将它添加到您在示例项目中设置的段落样式变量中,它与字距调整一起使用,不确定这是否适用于所有人字体,但它至少修复了您的示例项目:

paragraphStyle.lineHeightMultiple = 1.5

这种方法的唯一问题是它适用于只有一个单词或一行的行,您必须根据新行出现的时间进行字数调整以设置此“lineHeightMultiple”,这很糟糕,但是很明显,它不是一个很好的使用方法,但是如果你有一个 1 衬线字符串,如果你有更多,则需要调整,否则只需关闭字距调整,它会在没有这个线高倍数的情况下解决。

就好像行高在内部发生变化并将字符推到新行,但苹果不会自动考虑字符宽度的这种变化。

事实上,我认为您正在寻找的答案根本不是字距调整而是跟踪,这会将字母彼此分开。字距调整的问题在于,字距调整会影响字体的字形并覆盖它们的一些行为,因此它可能是我们在此处看到的明显效果。

来自苹果:

preferredFontForTextStyle:,返回的具体字体包括traits 根据用户偏好和上下文而有所不同,包括 跟踪(字母间距)调整,除了调整 由特定文本样式常量指定的用途。字体 使用文本样式常量返回的意思是用于所有文本 在用户界面元素(例如按钮)中的文本以外的应用程序中, 条和标签。自然,您需要选择看起来 就在您的应用程序中。观察情况也很重要 UIContentSizeCategoryDidChangeNotification 以便您可以重新布局 用户更改内容大小类别时的文本。当你的 应用程序收到该通知,它应该发送 向 Auto 定位的视图发送 invalidateIntrinsicContentSize 消息 布局或发送 setNeedsLayout 到定位的用户界面元素 手动。它应该使首选字体或字体描述符无效 并根据需要获取新的。

https://developer.apple.com/library/ios/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html

如果您确实需要字距调整,那么您可能应该调整连字的字距调整值(如果该字体可以使用)。

其他要考虑的事情,这确实有效,但它也很粗体,所以它已经不符合你上面的风格,但它是你可以玩弄的东西:

let sytemDynamicFontDescriptor = UIFontDescriptor.preferredFontDescriptorWithTextStyle(UIFontTextStyleHeadline)
let size = sytemDynamicFontDescriptor.pointSize
let myString1Font1 = UIFont(descriptor: sytemDynamicFontDescriptor, size:size)

println(sytemDynamicFontDescriptor.fontAttributes())

messageLabel.numberOfLines = 0
messageLabel.layer.cornerRadius = 10

messageLabel.attributedText = NSMutableAttributedString(
    string: "Thomas asdfad as ",
    // string: "Thomas says hello", // switch back to this to see it display the text properly on one
    attributes: [
        NSFontAttributeName: myString1Font1,
        NSForegroundColorAttributeName: UIColor.blackColor(),
        NSBackgroundColorAttributeName: UIColor.redColor(),
        NSKernAttributeName: 0.5,
        NSParagraphStyleAttributeName: paragraphStyle
    ]
)

【讨论】:

以上是关于带有段落样式行间距的 UILabel的主要内容,如果未能解决你的问题,请参考以下文章

怎么调整行间距

如何把keil中的代码行间距调宽

html行间距怎么设置

带有单行文本的 UILabel 的行间距

Smartform中表(table)的行间距设置

C# TextBox调整行间距