当改变UILabel字体时,计算UILabel大小不正确

7

我正在向一个视图添加一个UILabel,并满足以下布局要求:

  • 标签应该居中
  • 标签的最大字体大小为100点,但应缩小以适应。不应截断。
  • 标签的高度不应超过450点。
  • 另一个视图将直接位于标签下方。

我的标签属性布局约束似乎已经充分描述了这一点:

let label = UILabel()
label.backgroundColor = UIColor.yellowColor()
label.font = UIFont.systemFontOfSize(100)
label.numberOfLines = 0
label.textAlignment = .Center
label.adjustsFontSizeToFitWidth = true
label.text = "What's Brewing in PET/CT: CT and MR Emphasis"
label.setTranslatesAutoresizingMaskIntoConstraints(false)
view.addSubview(label)

view.addConstraint(NSLayoutConstraint(item: label, attribute: .CenterX, relatedBy: .Equal, toItem: view, attribute: .CenterX, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: label, attribute: .CenterY, relatedBy: .Equal, toItem: view, attribute: .CenterY, multiplier: 1, constant: 0))
view.addConstraint(NSLayoutConstraint(item: label, attribute: .Left, relatedBy: .Equal, toItem: view, attribute: .Left, multiplier: 1, constant: 60))
view.addConstraint(NSLayoutConstraint(item: label, attribute: .Right, relatedBy: .Equal, toItem: view, attribute: .Right, multiplier: 1, constant: -60))
view.addConstraint(NSLayoutConstraint(item: label, attribute: .Height, relatedBy: .LessThanOrEqual, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 450))

然而,我发现在像下面一样的较长内容中,标签会出现一些意外的间距:

enter image description here

这是不可接受的,因为任何放置在标签上方或下方的视图都会有一个间隙。

根据下面的讨论,我的工作理论是布局引擎基于字体大小100进行大小计算,而没有考虑到后续内容变长时的缩放。

如何满足上述布局要求(最好不使用任何已弃用的方法)?

这里是一个可供测试的测试项目


1
你似乎将高度设置为<= 450。从你的图片来看,UILabel在达到一定大小后不会自动缩小高度。然后它会在视图中垂直居中文本。你尝试过完全删除高度约束吗?因为它知道文本字段是居中的,所以应该可以让你不用它。如果它抱怨,请尝试将约束更改为> = 0而不是<= 450。 - Rory McKinnel
当您设置文本/字体时,可以尝试调用[label sizeToFit];。我不是一个熟练的Swift程序员,但我猜应该是label.sizeToFit() - Rory McKinnel
谢谢,但我已经尝试过了(连同setNeedsUpdateConstraints和setNeedsLayout) - Ben Packard
当您使用 .XXXXThanOrEqual 约束时,通常有助于使用另一个带有 .Equal 且优先级较低的约束条件,以便告诉布局管理器在哪个方向上进行缩放。因此,在您的情况下,请尝试使用高度等于 0 并将优先级设置为低。 - Simon Meyer
还可以尝试将“label.preferredMaxLayoutWidth”设置为该区域的宽度。这可能会影响高度计算。 - Rory McKinnel
显示剩余15条评论
2个回答

1

我看了一下您友好添加的测试项目。

问题是您设置的字体太大(100pt),而450点高度无法满足。您设置了 label.adjustsFontSizeToFitWidth = true,这会尽最大努力通过使用较小的字体来适应它。

因此,当您将字体设置为100时,它会根据原始文本截断并调整字体以适应宽度来调整文本区域的大小。这样,在压缩后留下顶部和底部的间隙,因为大小是基于原始字体计算的。

如果您使用像90这样的较小尺寸,它就可以正常工作,因为它适合小于450。

如果您删除 adjustsFontSizeToWith,它就可以正常工作并填充空间,但会截断文本。

如果您删除高度约束,它也可以正常工作,但其高度会大于450。

因此,您的问题很简单,测试字体对于450高度来说太大了。

可能的解决方案:

使用此处的答案如何获取UILabel(UITextView)自动调整字体大小?来询问所需的字体大小标签。

基本上,您可以使用:

CGFloat actualFontSize;
[label.text sizeWithFont:label.font
             minFontSize:label.minimumFontSize
          actualFontSize:&actualFontSize
                forWidth:label.bounds.size.width
           lineBreakMode:label.lineBreakMode];

这已经被弃用了,但这并不意味着你现在不能使用它。你需要将它转换成swift。

调用此函数,如果字体大小小于原始大小,则在标签中设置较小的字体大小。理论上,这应该可以完美地适应新的字体大小。

可能的解决方案更新:

我搜索了一下sizeWithFont的swift等效版本,有很多文章,但我没有看到哪个替换了上面提到的函数版本。

一个稍微低效的解决方案是,在你发布的测试项目中的addSubview代码之前添加以下内容(请原谅我的swift)。它基本上向后搜索字体大小,直到字体符合已知限制。这显然可以更加高效,但它表明如果在布局之前计算出正确的字体,则所有内容都会完美地适应450.0高度。

我在你的项目中测试了这个方法,即使在450.0高度时原本无法适应,现在文字也完美地适应了。

    // The current font size and name
    var fontSize:CGFloat = 120
    let fontName:String = "AvenirNext-Regular"

    // The size to fit is the frame width with 60 margin each size and height
    // of 450
    let fitSize:CGSize = CGSize(width:view.frame.width-120.0,height:450)
    var newSize:CGSize

    do{
        // Create the trial font
        label.font = UIFont(name: fontName, size: fontSize)

        // Calculate the size this would need based on the fitSize we want
        newSize = label.sizeThatFits(fitSize)

        // Make the font size smaller for next time around.
        fontSize-=0.5
    } while (newSize.height >= 450)

    println("Font size had to be reduced to \(fontSize)")

    view.addSubview(label)

但标签的文本是可变的,当长度较短时,100个点是合适的大小。这似乎就是“adjustsFontSizeToFitWidth”的全部意义所在?难道“fitting”计算不应该与“adjustFontSize”值一起工作,而不是假设截断?将字体大小更改为90将为我需要容纳的较长字符串复制相同的问题。 - Ben Packard
1
你可能会希望如此,但事实似乎并非如此。我认为这是因为它在视觉上将其缩小以适应宽度而不是高度。我已经更新了我的答案,并提供了一个可能的解决方案。我认为唯一让它正常工作的方法是在字体大小超过450时自行调整字体大小。 - Rory McKinnel
刚看到你的编辑和可能的解决方案,谢谢。我会试一下。另外考虑将行数设置为3或4而不是0,并删除高度约束。 - Ben Packard
@BenPackard 是的,你只有三个选择:要么按照描述缩放字体使其适合,要么放宽高度限制,或者限制行数。限制行数会导致文本被截断,这可能不是一个好事情。 - Rory McKinnel
@BenPackard发布了另一个更新,其中包含可以使用的Swift代码。已在您的项目中进行了测试。在添加子视图之前添加代码。可以更高效,但显示了一般方法将起作用。 - Rory McKinnel

1
我看了一下你的项目,可能已经找到了解决方案。
我在标签设置中添加了以下内容:
label.minimumScaleFactor = 0.5

这是我修改后的视图: 在此输入图片描述 标签上方不再有间距。
希望这可以帮助您解决问题!请告诉我它的效果如何。

谢谢,但我认为这只是对于这个特定字符串的幸运突破 - 例如,尝试将标签文本替换为“核医学2014年的当前争议,问题和技术 - 第一部分”,问题仍然存在。不过,对我来说,设置minimumScaleFactor会将布局从三行更改为四行确实很奇怪。如果有什么区别的话,由于字体大小只能通过添加此内容来减小(或保持不变),我期望标签在更少或相同数量的行上断开 - 而不是更多。也许这可以帮助指导某人朝着正确的方向前进。 - Ben Packard
没错,如果你改变minimumFontScale的值,它也可以起作用。但显然这不应该是这样的。我在查看你的问题时在UILabel类中发现了这个问题:var adjustsFontSizeToFitWidth: Bool // default is NO // deprecated - hand tune by using NSKernAttributeName to affect tracking // NOTE: deprecated - use minimumScaleFactor. default is 0.0,所以可能是框架中的一个错误。 - Catalina T.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接