调整 UILabel 的大小以适应插图

Posted

技术标签:

【中文标题】调整 UILabel 的大小以适应插图【英文标题】:Resizing a UILabel to accommodate insets 【发布时间】:2014-02-05 16:21:00 【问题描述】:

我正在构建一个屏幕来扫描条形码,我需要在一些 UILabels 后面放置一个半透明屏幕以提高在浅色背景下的可见度。

现在的屏幕是这样的:

我在UILabel 上设置背景颜色以获取半透明框。我还创建了一个自定义 UILabel 子类,以允许我在 UILabel 的边缘和使用 this approach 的文本之间设置一些填充。

正如您在上面的屏幕中看到的,UILabel 没有正确调整大小以考虑填充。 “填充”只是在不改变标签宽度的情况下移动文本,导致文本被截断。

这两个标签都将包含任意长度的文本,我真的需要UILabel 来动态调整大小。

我可以重写什么UILabel 方法来增加标签的宽度并考虑填充?

【问题讨论】:

这是如何加载的?如果您使用的是故事板,您是否使用它加载了此标签?如果你这样做了,你有没有给这个标签添加任何约束并将大小设置为一个常数? 好问题。这个视图完全是用代码构建的。我使用约束来布局屏幕,标签上唯一的约束设置屏幕上的位置,而不是标签的大小。 好的,这很可能只是文本太长的问题。我想说只是让标签完全变宽,甚至根据文本在其中的长度调整标签的宽度 根据文本长度调整标签的宽度正是我所追求的,但是在 UILabel 之外计算是愚蠢的。我希望了解我可以重写什么 UILabel 方法来重新计算标签的大小以合并填充。 看看这个:***.com/questions/13237353/… 【参考方案1】:

这是一个正确计算尺寸的标签类。 posted code 在 Swift 3 中,但您也可以下载 Swift 2 或 Objective-C 版本。

它是如何工作的?

通过计算正确的 textRect,所有sizeToFit 和自动布局的东西都可以按预期工作。诀窍是先减去 insets,然后计算原始标签边界,最后再次添加 insets。

代码 (Swift 5)

class NRLabel: UILabel 
    var textInsets = UIEdgeInsets.zero 
        didSet  invalidateIntrinsicContentSize() 
    
    
    override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect 
        let insetRect = bounds.inset(by: textInsets)
        let textRect = super.textRect(forBounds: insetRect, limitedToNumberOfLines: numberOfLines)
        let invertedInsets = UIEdgeInsets(
            top: -textInsets.top,
            left: -textInsets.left,
            bottom: -textInsets.bottom,
            right: -textInsets.right
        )
        return textRect.inset(by: invertedInsets)
    
    
    override func drawText(in rect: CGRect) 
        super.drawText(in: rect.inset(by: textInsets))
    

可选:界面生成器支持

如果您想在故事板中设置文本插入,您可以使用以下扩展来启用 Interface Builder 支持:

@IBDesignable
extension NRLabel 

    // currently UIEdgeInsets is no supported IBDesignable type,
    // so we have to fan it out here:
    @IBInspectable
    var leftTextInset: CGFloat 
        set  textInsets.left = newValue 
        get  return textInsets.left 
    

    // Same for the right, top and bottom edges.

现在您可以方便地在 IB 中设置插图,然后只需按 ⌘= 即可调整标签的大小以适合。

免责声明:

所有代码都在公共领域。随心所欲。

【讨论】:

清关;而不是手动设置 rect 的偏移量,您可以使用 CGRectInset 代替。像这样:return CGRectInset(rect, -insets.left, insets.top);developer.apple.com/library/mac/documentation/graphicsimaging/… @Petter 使用 CGRectInset 仅在插图对称时有效。 @NikolaiRuhe 我们是否也需要覆盖 intrinsicContentSize() ?我在某处看到 AL 需要它。也许你的伎俩不涉及这种方法? 我建议添加 ` if text == nil return super.textRectForBounds(bounds, limitedToNumberOfLines: numberOfLines) ` 在没有文本时“隐藏”标签。 ( 我们必须在主类中调用一些函数吗?将此代码复制到 UILabel 类似乎不起作用【参考方案2】:

这是 UILabel 子类的 Swift 版本(与@Nikolai 的回答相同),它在 UILabel 的文本周围创建了一个额外的填充:

class EdgeInsetLabel : UILabel 
    var edgeInsets:UIEdgeInsets = UIEdgeInsetsZero

    override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect 
        var rect = super.textRectForBounds(UIEdgeInsetsInsetRect(bounds, edgeInsets), limitedToNumberOfLines: numberOfLines)

        rect.origin.x -= edgeInsets.left
        rect.origin.y -= edgeInsets.top
        rect.size.width  += (edgeInsets.left + edgeInsets.right);
        rect.size.height += (edgeInsets.top + edgeInsets.bottom);

        return rect
    

    override func drawTextInRect(rect: CGRect) 
        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, edgeInsets))
    

【讨论】:

您能否在回答中澄清“解决问题”的含义?无论如何,使用你的代码并投票回答。 谢谢!我更喜欢您的代码,因为它不需要扩展 - 它是一个简单的子类。嗯 - 你可能也想让子类“最终”! 这不会覆盖固有大小 - 因此如果您使用自动调整大小的流布局,它将截断文本 覆盖 textRectForBounds 修复了截断问题,谢谢!【参考方案3】:

这是基于 Nikolai 代码的 C# 版本(对 Xamarin 有用):

public class UIEdgeableLabel : UILabel

    public UIEdgeableLabel() : base()  
    public UIEdgeableLabel(NSCoder coder) : base(coder)  
    public UIEdgeableLabel(CGRect frame) : base(frame)  
    protected UIEdgeableLabel(NSObjectFlag t) : base(t)  

    private UIEdgeInsets _edgeInset = UIEdgeInsets.Zero;
    public UIEdgeInsets EdgeInsets
    
        get  return _edgeInset; 
        set
        
            _edgeInset = value;
            this.InvalidateIntrinsicContentSize();
        
    

    public override CGRect TextRectForBounds(CGRect bounds, nint numberOfLines)
    
        var rect = base.TextRectForBounds(EdgeInsets.InsetRect(bounds), numberOfLines);
        return new CGRect(x: rect.X - EdgeInsets.Left,
                          y: rect.Y - EdgeInsets.Top,
                          width: rect.Width + EdgeInsets.Left + EdgeInsets.Right,
                          height: rect.Height + EdgeInsets.Top + EdgeInsets.Bottom);
    

    public override void DrawText(CGRect rect)
    
        base.DrawText(this.EdgeInsets.InsetRect(rect));
    

【讨论】:

【参考方案4】:

Swift 5 版本 Nikolai Ruhe 回答:

extension UIEdgeInsets 
   func apply(_ rect: CGRect) -> CGRect 
      return rect.inset(by: self)
   


class EdgeInsetLabel: UILabel 
  var textInsets = UIEdgeInsets.zero 
      didSet  invalidateIntrinsicContentSize() 
  

  override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect 
    let insetRect = bounds.inset(by: textInsets)
    let textRect = super.textRect(forBounds: insetRect, limitedToNumberOfLines: numberOfLines)
    let invertedInsets = UIEdgeInsets(top: -textInsets.top,
                                      left: -textInsets.left,
                                      bottom: -textInsets.bottom,
                                      right: -textInsets.right)
    return textRect.inset(by: invertedInsets)
  

  override func drawText(in rect: CGRect) 
      super.drawText(in: rect.inset(by: textInsets))
  

【讨论】:

【参考方案5】:

除了 Nikolai Ruhe 的回答之外,您需要使自动布局的内在内容大小无效,以正确重新计算大小更改。如果您在应用程序生命周期内更改 edgeInsets,您会注意到此问题:

class NRLabel: UILabel 

    var edgeInsets = UIEdgeInsetsZero 
        didSet 
            self.invalidateIntrinsicContentSize()
        
    

    ...

【讨论】:

【参考方案6】:

这是一个示例,我使用圆角标签左右两侧的简单 10 单位填充。只需将标签文本设置为自身居中并使其成为 IndentedLabel 类,其余部分自行处理。要修改填充,只需放大或缩小 rect.size.width += (x)

class IndentedLabel: UILabel 

    var edgeInsets:UIEdgeInsets = UIEdgeInsetsZero

    override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect 
        var rect = super.textRectForBounds(UIEdgeInsetsInsetRect(bounds, edgeInsets), limitedToNumberOfLines: numberOfLines)

        rect.size.width  += 20;

        return rect
    

    override func drawTextInRect(rect: CGRect) 
        self.clipsToBounds = true
        self.layer.cornerRadius = 3
        super.drawTextInRect(UIEdgeInsetsInsetRect(rect, edgeInsets))
    

【讨论】:

【参考方案7】:

这是一种快速、简单的方法,您可以更快地理解。它不像 Nikolai 那样强大,但它可以完成工作。当我试图将我的文本放入 UITableViewCell 中的 UILabel 中时,我这样做了:

    为 UILabel 设置宽度限制 通过 IBOutlet 将约束连接到您的代码,无论是 VC(如果您正在执行扩展表格视图单元格,则自定义单元格类) 为文本的实际大小创建一个变量,然后将 insets + 宽度大小添加到约束并更新视图:

let messageTextSize: CGSize = (messageText as NSString).sizeWithAttributes([ NSFontAttributeName: UIFont.systemFontOfSize(14.0)]) cell.widthConstraint.constant = messageTextSize.width + myInsetsOrWhatever

我还没有对它进行广泛的测试,您可能需要使用您添加的确切 CGFloat 值。我发现正确的尺寸并不完全是宽度加上插图;它比那个大一点。这样可以确保 UILabel 的宽度始终至少为文本大小或更大。

【讨论】:

【参考方案8】:

斯威夫特 5 。 您可以创建自定义 UILabel 类。 我在内容的左侧添加了 22 个填充。当UILabel 要求intrinsicContentSize 通过添加您添加的填充大小返回时,我添加了 22 并返回了自定义大小。就是这样。

// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation. 
override func draw(_ rect: CGRect) 
        // Drawing code
        let insets = UIEdgeInsets(top: 0, left: 22, bottom: 0, right: 0)
        super.drawText(in: rect.inset(by: insets))
        self.layoutSubviews()


// This will return custom size with flexible content size. Mainly it can be used in Chat.
override var intrinsicContentSize: CGSize 
        var size = super.intrinsicContentSize
        size.width = 22 + size.width
        return size

【讨论】:

以上是关于调整 UILabel 的大小以适应插图的主要内容,如果未能解决你的问题,请参考以下文章

如何调整字体大小以适应 UILabel 的高度和宽度

为啥在 Interface Builder 中更改字体大小会阻止 UILabel 调整大小以适应内容?

调整UILabel的大小以适应字体大小和文本

调整UILabel的大小以适应Word Wrap

调整 UILabel 的大小以适应自定义 UITableViewCell 中的文本,无论宽度如何

调整文本大小以适应宽度(1行标签)后,如何在 UILabel 中垂直和水平居中?