【问题标题】:Adding space/padding to a UILabel向 UILabel 添加空间/填充
【发布时间】:2015-02-12 02:45:12
【问题描述】:

我有一个UILabel,我想在顶部和底部添加空间。使用约束中的最小高度,我将其修改为:

为此,我使用过:

override func drawTextInRect(rect: CGRect) {
    var insets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10.0)
    super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
}

但我必须找到一种不同的方法,因为如果我写多于两行,问题是一样的:

【问题讨论】:

  • 我们终于弄清楚了如何在所有动态情况下正确执行此操作,作为 UILabel 的完美替代品,无需重新布局或任何其他问题。呸。 stackoverflow.com/a/58876988/294884

标签: ios uilabel padding


【解决方案1】:

带有 UILabel 扩展的 Swift 5 示例

使用下面的代码设置您的边距就像label.setMargins(15) 一样简单。

extension UILabel {
    func setMargins(_ margin: CGFloat = 10) {
        if let textString = self.text {
            let paragraphStyle = NSMutableParagraphStyle()
            paragraphStyle.firstLineHeadIndent = margin
            paragraphStyle.headIndent = margin
            paragraphStyle.tailIndent = -margin
            let attributedString = NSMutableAttributedString(string: textString)
            attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
            attributedText = attributedString
        }
    }
}

【讨论】:

    【解决方案2】:

    就像其他答案一样,但它修复了一个错误:

    label.width被自动布局控制时,有时文字会被裁剪。

    @IBDesignable
    class InsetLabel: UILabel {
    
        @IBInspectable var topInset: CGFloat = 4.0
        @IBInspectable var leftInset: CGFloat = 4.0
        @IBInspectable var bottomInset: CGFloat = 4.0
        @IBInspectable var rightInset: CGFloat = 4.0
    
        var insets: UIEdgeInsets {
            get {
                return UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
            }
            set {
                topInset = newValue.top
                leftInset = newValue.left
                bottomInset = newValue.bottom
                rightInset = newValue.right
            }
        }
    
        override func sizeThatFits(_ size: CGSize) -> CGSize {
            var adjSize = super.sizeThatFits(size)
            adjSize.width += leftInset + rightInset
            adjSize.height += topInset + bottomInset
            return adjSize
        }
    
        override var intrinsicContentSize: CGSize {
            let systemContentSize = super.intrinsicContentSize
            let adjustSize = CGSize(width: systemContentSize.width + leftInset + rightInset, height: systemContentSize.height + topInset +     bottomInset)
            if adjustSize.width > preferredMaxLayoutWidth && preferredMaxLayoutWidth != 0 {
                let constraintSize = CGSize(width: bounds.width - (leftInset + rightInset), height: .greatestFiniteMagnitude)
                let newSize = super.sizeThatFits(constraintSize)
                return CGSize(width: systemContentSize.width, height: ceil(newSize.height) + topInset + bottomInset)
            } else {
                return adjustSize
            }
        }
    
        override func drawText(in rect: CGRect) {
            super.drawText(in: rect.inset(by: insets))
        }
    }
    

    【讨论】:

    • 解释一下。例如,修复的想法/要点是什么?改变或增加了什么?请通过editing (changing) your answer 回复,而不是在 cmets 中(without "Edit:"、"Update:" 或类似的 - 答案应该看起来像是今天写的)。
    【解决方案3】:

    如果您在应用填充时遇到文本修剪问题,请使用此代码。

    @IBDesignable class PaddingLabel: UILabel {
    
        @IBInspectable var topInset: CGFloat = 5.0
        @IBInspectable var bottomInset: CGFloat = 5.0
        @IBInspectable var leftInset: CGFloat = 5.0
        @IBInspectable var rightInset: CGFloat = 5.0
    
        override func drawText(in rect: CGRect) {
            let insets = UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
            super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
        }
    
        override var intrinsicContentSize: CGSize {
            var intrinsicSuperViewContentSize = super.intrinsicContentSize
            let textWidth = frame.size.width - (self.leftInset + self.rightInset)
            let newSize = self.text!.boundingRect(with: CGSize(textWidth, CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: self.font], context: nil)
            intrinsicSuperViewContentSize.height = ceil(newSize.size.height) + self.topInset + self.bottomInset
            return intrinsicSuperViewContentSize
        }
    }
    
    extension CGSize{
        init(_ width:CGFloat,_ height:CGFloat) {
            self.init(width:width,height:height)
        }
    }
    

    【讨论】:

    • 感谢您的发帖,我正在寻找关于填充 + 修剪的解决方案。在我看来,您的解决方案打破了我需要的 label.numberOfLines = 0 。有什么解决方法吗?
    • @Don 在所有情况下都是唯一正确的解决方案:stackoverflow.com/a/58876988/294884
    【解决方案4】:

    关于Mundi's answer的详细说明。

    即,在UIView 中嵌入标签并通过自动布局强制填充。示例:

    概述:

    1. 创建一个UIView(“面板”),并设置它的外观。

    2. 创建一个UILabel 并将其添加到面板中。

    3. 添加约束以强制填充。

    4. 将面板添加到视图层次结构中,然后定位面板。

    详情:

    1. 创建面板视图。

      让面板 = UIView() panel.backgroundColor = .green panel.layer.cornerRadius = 12

    2. 创建标签,将其作为子视图添加到面板中。

      让标签 = UILabel() panel.addSubview(label)

    3. 在标签边缘和面板之间添加约束。这会强制面板与标签保持距离。即“填充”。

    社论:手工完成所有这些工作非常繁琐、冗长且容易出错。我建议你从 GitHub 中选择一个 Auto Layout 包装器或自己编写一个

    label.panel.translatesAutoresizingMaskIntoConstraints = false
    label.topAnchor.constraint(equalTo: panel.topAnchor,
        constant: vPadding).isActive = true
    label.bottomAnchor.constraint(equalTo: panel.bottomAnchor,
        constant: -vPadding).isActive = true
    label.leadingAnchor.constraint(equalTo: panel.leadingAnchor,
        constant: hPadding).isActive = true
    label.trailingAnchor.constraint(equalTo: panel.trailingAnchor,
        constant: -hPadding).isActive = true
    
    label.textAlignment = .center
    
    1. 将面板添加到视图层次结构中,然后添加定位约束。例如,拥抱 tableViewCell 的右侧,如示例图片所示。

    注意:你只需要添加位置约束,不需要添加维度约束:Auto Layout 会根据标签的intrinsicContentSize 和之前添加的约束来解决布局。

    hostView.addSubview(panel)
    panel.translatesAutoresizingMaskIntoConstraints = false
    panel.trailingAnchor.constraint(equalTo: hostView.trailingAnchor,
        constant: -16).isActive = true
    panel.centerYAnchor.constraint(equalTo: hostView.centerYAnchor).isActive = true
    

    【讨论】:

      【解决方案5】:

      如果你想坚持使用 UILabel,而不是继承它,Mundi has given you a clear solution

      如果您愿意避免使用 UIView 包装 UILabel,则可以使用 UITextView 来启用 UIEdgeInsets(填充)或子类 UILabel 以支持 UIEdgeInsets。

      使用 UITextView 只需要提供插图(Objective-C):

      textView.textContainerInset = UIEdgeInsetsMake(10, 0, 10, 0);
      

      或者,如果您将 UILabel 子类化,则此方法的一个示例将覆盖 drawTextInRect 方法
      (目标-C)

      - (void)drawTextInRect:(CGRect)uiLabelRect {
          UIEdgeInsets myLabelInsets = {10, 0, 10, 0};
          [super drawTextInRect:UIEdgeInsetsInsetRect(uiLabelRect, myLabelInsets)];
      }
      

      您还可以为新的子类 UILabel 提供 TOP、LEFT、BOTTOM 和 RIGHT 的插入变量。

      示例代码可以是:

      在 .h 中(Objective-C)

      float topInset, leftInset,bottomInset, rightInset;
      

      .m(Objective-C)

      - (void)drawTextInRect:(CGRect)uiLabelRect {
          [super drawTextInRect:UIEdgeInsetsInsetRect(uiLabelRect, UIEdgeInsetsMake(topInset,leftInset,bottomInset,rightInset))];
      }
      

      据我所知,在子类化 UILabel 时,您似乎必须覆盖它的 intrinsicContentSize。

      所以你应该重写 intrinsicContentSize 像:

      - (CGSize) intrinsicContentSize {
          CGSize intrinsicSuperViewContentSize = [super intrinsicContentSize] ;
          intrinsicSuperViewContentSize.height += topInset + bottomInset ;
          intrinsicSuperViewContentSize.width += leftInset + rightInset ;
          return intrinsicSuperViewContentSize ;
      }
      

      并添加以下方法来编辑您的插图,而不是单独编辑它们:

      - (void) setContentEdgeInsets:(UIEdgeInsets)edgeInsets {
          topInset = edgeInsets.top;
          leftInset = edgeInsets.left;
          rightInset = edgeInsets.right;
          bottomInset = edgeInsets.bottom;
          [self invalidateIntrinsicContentSize] ;
      }
      

      它将更新您的 UILabel 的大小以匹配边缘插图并涵盖您提到的多行必要性。

      经过一番搜索,我发现了这个带有 IPInsetLabel 的 Gist。如果这些解决方案都不起作用,您可以尝试一下。

      关于这个问题有一个类似的问题(重复)。
      有关可用解决方案的完整列表,请参阅此答案:UILabel text margin

      【讨论】:

      • 对不起,我已经用过:` override func drawTextInRect(rect: CGRect) { var insets: UIEdgeInsets = UIEdgeInsets(top: 0.0, left: 10.0, bottom: 0.0, right: 10.0) super .drawTextInRect(UIEdgeInsetsInsetRect(rect, insets)) } `它不起作用,因为结果相同,不要动态工作..
      • 你试过用 UITextView 代替 UILabel 吗?或者你真的需要使用 UILabel 吗?
      • @Annachiara 检查我所做的编辑。看看它是否有效。
      • 好的。它对文本视图有效吗?很抱歉没有用 Swift 编写,但我仍然处于 Obj-C 模式。我使用该代码的目标是帮助您得出一些结论。希望是的。
      • 使用 TextView 和一些故事板设置和 self.textview.textContainerInset = UIEdgeInsetsMake(0, 10, 10, 10);它终于奏效了!谢谢!
      【解决方案6】:

      我还没有在任何地方看到这个答案。我的技巧是在标签上设置一个宽度约束,并在设置标签文本时调整该宽度。

      self.myLabel.text = myString;
      
      UIFont * const font = [UIFont systemFontOfSize:17 weight:UIFontWeightRegular]; // Change to your own label font.
      
      CGSize const size = CGSizeMake(INFINITY, 18); // 18 is height of label.
      
      CGFloat const textWidth = [myString boundingRectWithSize:size options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: font} context:nil].size.width;
      
      self.myLabelWidthConstraint.constant = textWidth + 20; // 10 padding on each side. Also, set text alignment to centre.
      

      【讨论】:

        【解决方案7】:

        我的解决方案与人们的回答类似,但添加了sizeThatFits 以帮助UIKit 找出合适的尺寸。

        class InsetLabel : UILabel {
            @objc var textInsets: UIEdgeInsets = .zero
        
            override func drawText(in rect: CGRect) {
                super.drawText(in: rect.inset(by: textInsets))
            }
        
            override func sizeThatFits(_ size: CGSize) -> CGSize {
                var s = super.sizeThatFits(CGSize(width: size.width - (textInsets.left + textInsets.right), height: size.height - (textInsets.top + textInsets.bottom)))
                s.height += textInsets.top + textInsets.bottom
                return s
            }
        }
        

        【讨论】:

          【解决方案8】:

          Objective-C

          基于Tai Le's answer,它在Interface Builder Designable 中实现了该功能,这里是Objective-C 版本。

          把这个放在 YourLabel.h 文件中:

          @interface YourLabel : UILabel
          
          @property IBInspectable CGFloat topInset;
          @property IBInspectable CGFloat bottomInset;
          @property IBInspectable CGFloat leftInset;
          @property IBInspectable CGFloat rightInset;
          
          @end
          

          这将进入文件 YourLabel.m

          IB_DESIGNABLE
          
          @implementation YourLabel
          
          #pragma mark - Super
          
          - (instancetype)initWithCoder:(NSCoder *)aDecoder {
              self = [super initWithCoder:aDecoder];
              if (self) {
                  self.topInset = 0;
                  self.bottomInset = 0;
                  self.leftInset = 0;
                  self.rightInset = 0;
              }
              return self;
          }
          
          - (void)drawTextInRect:(CGRect)rect {
              UIEdgeInsets insets = UIEdgeInsetsMake(self.topInset, self.leftInset, self.bottomInset, self.rightInset);
              [super drawTextInRect:UIEdgeInsetsInsetRect(rect, insets)];
          }
          
          - (CGSize)intrinsicContentSize {
              
              CGSize size = [super intrinsicContentSize];
              return CGSizeMake(size.width + self.leftInset + self.rightInset,
                                size.height + self.topInset + self.bottomInset);
          }
          
          @end
          

          XIB 文件或情节提要中指定类后,您可以直接在 Interface Builder 中修改 YourLabel 插入,插入的默认值为零。

          【讨论】:

            【解决方案9】:

            Swift 4+

            class EdgeInsetLabel: UILabel {
                var textInsets = UIEdgeInsets.zero {
                    didSet { invalidateIntrinsicContentSize() }
                }
            
                override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
                    let textRect = super.textRect(forBounds: bounds, 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))
                }
            }
            

            用法:

            let label = EdgeInsetLabel()
            label.textInsets = UIEdgeInsets(top: 2, left: 6, bottom: 2, right: 6)
            

            【讨论】:

            • WAIT - 在某些情况下,我实际上发现了一个问题。以前这是最正确的答案。我在下面输入了正确的答案。
            • 我的答案中包含了一张图片来显示问题
            【解决方案10】:

            仅适用于 SINGLE-LINE 标签:(2021 语法)

            对于在此处进行谷歌搜索但只需要在严格单行标签(例如部分标题或其他列表项)上进行填充的任何人,

            语法发生了很大变化。这是要复制和粘贴的确切类:

            // add 100 above, 50 padding below a SINGLE-LINE label
            import UIKit
            class SingleLineLabelWithSpacing: UILabel {
                // STRICTLY for SINGLE LINE labels
                // only works with SINGLE LINE labels
            
                override func drawText(in rect: CGRect) {
                    let insets: UIEdgeInsets = UIEdgeInsets(
                           top: 100, left: 0, bottom: 50, right: 0)
                    super.drawText(in: rect.inset(by: insets))
                }
            
                override var intrinsicContentSize: CGSize {
                    var ic = super.intrinsicContentSize
                    ic.height = ic.height + 150
                    return ic
                }
            }
            

            注意 100/50 上方/下方的填充。

            当您有任何类型的滚动列表、提要或其他列表时,这是通常要做的事情。

            通过这种方式,您无需考虑标题、用户名等上方/下方的间距 - 您只需将其放在堆栈视图中或其他任何情况下即可。

            当然,当设计师想要调整这两个值时,您当然可以在各处同时更改这两个值。

            提醒:如果你想真正填充 UILabel 使其完美地工作而不管文本行数、动态调整单元格大小等等等等,它是非常复杂的。正确答案在“适用于所有情况的完整且正确的解决方案”下方。

            【讨论】:

              【解决方案11】:

              我们终于想出了一个完整且正确的解决方案,它适用于所有情况,包括堆栈视图、动态单元格、动态行数、集合视图、动画填充、每个字符计数以及所有其他情况。

              填充UILabel,完整解决方案。 2021 年更新。

              事实证明,三件事是必须要做的。

              1。必须使用新的较小尺寸调用 textRect#forBounds

              2。必须用新的较小尺寸覆盖 drawText

              3。如果是动态大小的单元格,必须调整intrinsicContentSize

              在下面的典型示例中,文本单元位于表格视图、堆栈视图或类似结构中,这使其具有固定宽度。在示例中,我们希望填充 60,20,20,24。

              因此,我们采用“现有的”intrinsicContentSize实际上将高度加 80

              重复...

              您必须从字面上“获取”引擎“到目前为止”计算的高度,然后更改该值。

              我发现这个过程令人困惑,但是,它就是这样工作的。对我来说,Apple 应该公开一个名为“初步高度计算”之类的调用。

              其次,我们必须实际使用 textRect#forBounds 调用我们新的更小的尺寸

              所以在 textRect#forBounds 中,我们首先缩小尺寸,然后调用 super。

              警告!你必须之后调用super,而不是之前!

              如果您仔细调查此页面上的所有尝试和讨论,那就是确切的问题。

              请注意一些解决方案“似乎通常有效”。这确实是确切的原因 - 令人困惑的是,您必须“事后调用 super”,而不是之前。

              如果您“以错误的顺序”调用 super,它通常可以工作,但对于某些特定的文本长度会失败

              这是一个“错误地先做超级”的直观示例:

              注意 60,20,20,24 的边距是正确的但大小计算实际上是错误的,因为它是使用 textRect#forBounds 中的“超级优先”模式完成的。

              固定:

              直到现在 textRect#forBounds 引擎知道如何正确地进行计算

              终于!

              同样,在本例中,UILabel 用于宽度固定的典型情况。所以在 intrinsicContentSize 中,我们必须“添加”我们想要的整体额外高度。 (您不需要以任何方式“添加”宽度,因为它是固定的,这将毫无意义。)

              然后在 textRect#forBounds 中,您会通过自动布局获得“到目前为止建议的”边界,减去您的边距,然后 strong> 再次调用 textRect#forBounds 引擎,也就是在 super 中,它会给你一个结果。

              最后,在 drawText 中,您当然可以在同一个较小的框中进行绘制。

              呼!

              let UIEI = UIEdgeInsets(top: 60, left: 20, bottom: 20, right: 24) // as desired
              
              override var intrinsicContentSize:CGSize {
                  numberOfLines = 0       // don't forget!
                  var s = super.intrinsicContentSize
                  s.height = s.height + UIEI.top + UIEI.bottom
                  s.width = s.width + UIEI.left + UIEI.right
                  return s
              }
              
              override func drawText(in rect:CGRect) {
                  let r = rect.inset(by: UIEI)
                  super.drawText(in: r)
              }
              
              override func textRect(forBounds bounds:CGRect,
                                         limitedToNumberOfLines n:Int) -> CGRect {
                  let b = bounds
                  let tr = b.inset(by: UIEI)
                  let ctr = super.textRect(forBounds: tr, limitedToNumberOfLines: 0)
                  // that line of code MUST be LAST in this function, NOT first
                  return ctr
              }
              

              再一次。请注意,“几乎”正确的这个和其他 QA 的答案会遇到上面第一张图片中的问题 - “super is in the wrong place”。您必须在intrinsicContentSize 中强制增大尺寸,然后在textRect#forBounds 中必须首先缩小第一个建议范围然后 调用 super。

              总结:必须在textRect#forBounds中“调用super last

              这就是秘密。

              请注意,您不需要也不应该额外调用 invalidate、sizeThatFits、needsLayout 或任何其他强制调用。正确的解决方案应该在正常的自动布局绘制周期中正常工作。

              【讨论】:

              • 很好的解决方案。您能否详细说明为什么要覆盖numberOfLines
              • 谢谢!确实花费了很多时间来实现它:/(在 iOS 中,“numberOfLines 零”意味着将文本换行。我只是在示例代码中设置它,因为人们可能会忘记在情节提要上设置它!)跨度>
              • 我使用了这个和来自最高投票(但似乎不正确)答案 (stackoverflow.com/a/32368958/826946) 的 IBInspectable 内容,并添加了对边框(颜色、厚度、cornerRadius)的支持,也使用 IBInspectable。在这里:stackoverflow.com/a/67317976/826946
              • 喜欢 2021 年,您仍然无法在 UILabel 上设置属性以自动执行此操作。
              • 我已经尝试过这种方法,但是之后我无法动态设置字体大小?尽管我将其设置为更高的值,例如 24,但它似乎缩小到 10。当我禁用此代码时,大小是正确的。我应该补充一点,我在一个表格视图中,其中有一个单元格和标签,我认为它的内部大小通过填充和行间距进行了调整。
              【解决方案12】:

              如果你想使用UILabel

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

              【讨论】:

                【解决方案13】:

                我已经在 Swift 4.2 上尝试过,希望它对你有用!

                @IBDesignable class PaddingLabel: UILabel {
                
                    @IBInspectable var topInset: CGFloat = 5.0
                    @IBInspectable var bottomInset: CGFloat = 5.0
                    @IBInspectable var leftInset: CGFloat = 7.0
                    @IBInspectable var rightInset: CGFloat = 7.0
                
                    override func drawText(in rect: CGRect) {
                        let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
                        super.drawText(in: rect.inset(by: insets))
                    }
                
                    override var intrinsicContentSize: CGSize {
                        let size = super.intrinsicContentSize
                        return CGSize(width: size.width + leftInset + rightInset,
                                      height: size.height + topInset + bottomInset)
                    }   
                
                    override var bounds: CGRect {
                        didSet {
                            // ensures this works within stack views if multi-line
                            preferredMaxLayoutWidth = bounds.width - (leftInset + rightInset)
                        }
                    } 
                }
                

                或者你可以在这里使用 CocoaPods https://github.com/levantAJ/PaddingLabel

                pod 'PaddingLabel', '1.2'

                【讨论】:

                • uilabel宽度没有变化,导致文字变成“...”
                • @Tai Le ,谢谢分享,我在tableview中用过,不知道为什么要修剪文字,例如。学生成为学生,
                • @Tim 也许你打算使用min
                • 这里有个警告。我一直在 UILabel 子类中使用这个解决方案。在多行模式下使用这些标签时,在垂直 UIStackView 中,会出现问题。有时标签似乎在没有正确调整标签大小的情况下包装了文本 - 因此字符串末尾缺少一个或 2 个单词。我现在没有解决办法。如果我做一个,我会在这里写下来。在证明它就在这里之前,我花了几个小时研究这个问题。
                • 要使其在这些情况下工作,您需要覆盖“setBounds”并将 self.preferredMaxLayoutWidth 设置为边界的宽度,减去您的左右插图
                【解决方案14】:

                如果您正在寻找左右填充,您可以简单地在文本前后添加空格:

                titleLabel.text = " \(categoryName) "
                

                【讨论】:

                • 这不起作用。 iOS 实际上删除任何尾随空格。这是一个关于它的讨论:stackoverflow.com/a/59813420/294884
                • 它有效,请查看更新的答案并更改您的反对票
                • Badr,它确实删除了 TRAILING 空格,意思是,在字符串的末尾。 = " \(categoryName) " .. 试试看
                • 它仍然有效,我更新了答案。它可能不是空格的确切数量,但在我的情况下,前导和尾随空格都有效。
                【解决方案15】:

                斯威夫特 5

                在 layoutSubviews() 方法中,将 UILabel 的插入更改如下。

                  override func layoutSubviews() {
                    super.layoutSubviews()
                
                    label.frame = label.frame.inset(by: UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10))
                  }
                

                【讨论】:

                • @Fattie 它适用于我,如果其他人提出相同的答案,它可能适用于其他人。
                • Ciao Kivia,老实说,恐怕它不起作用 - 它仅适用于不属于布局的最简单的测试示例,标签不受任何方式约束到它周围的其他元素,并且文本不是即时设置的。 iOS 太痛苦了!!!!
                【解决方案16】:

                SWIFT 4

                易于使用的解决方案,适用于项目中的所有 UILabel 子项。

                示例:

                let label = UILabel()
                    label.<Do something>
                    label.padding = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 0)
                

                UILabel 扩展

                import UIKit
                
                extension UILabel {
                    private struct AssociatedKeys {
                        static var padding = UIEdgeInsets()
                    }
                
                    public var padding: UIEdgeInsets? {
                        get {
                            return objc_getAssociatedObject(self, &AssociatedKeys.padding) as? UIEdgeInsets
                        }
                        set {
                            if let newValue = newValue {
                                objc_setAssociatedObject(self, &AssociatedKeys.padding, newValue as UIEdgeInsets?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
                            }
                        }
                    }
                
                    override open func draw(_ rect: CGRect) {
                        if let insets = padding {
                            self.drawText(in: rect.inset(by: insets))
                        } else {
                            self.drawText(in: rect)
                        }
                    }
                
                    override open var intrinsicContentSize: CGSize {
                        guard let text = self.text else { return super.intrinsicContentSize }
                
                        var contentSize = super.intrinsicContentSize
                        var textWidth: CGFloat = frame.size.width
                        var insetsHeight: CGFloat = 0.0
                        var insetsWidth: CGFloat = 0.0
                
                        if let insets = padding {
                            insetsWidth += insets.left + insets.right
                            insetsHeight += insets.top + insets.bottom
                            textWidth -= insetsWidth
                        }
                
                        let newSize = text.boundingRect(with: CGSize(width: textWidth, height: CGFloat.greatestFiniteMagnitude),
                                                        options: NSStringDrawingOptions.usesLineFragmentOrigin,
                                                        attributes: [NSAttributedString.Key.font: self.font], context: nil)
                
                        contentSize.height = ceil(newSize.size.height) + insetsHeight
                        contentSize.width = ceil(newSize.size.width) + insetsWidth
                
                        return contentSize
                    }
                }
                

                【讨论】:

                • 请简要说明你的答案,不要只贴代码。
                • 你的分机取消0值到numberOfLines
                • 这很好,但是我遇到了多行问题,即使我添加了第 2 行或保留为 0,它总是显示一个。你知道怎么解决吗?
                • @AntoineBodart 你解决了 numberOfLines 问题吗?
                • 不要使用扩展覆盖方法。这是非常糟糕的做法。请参阅此处了解更多信息:stackoverflow.com/a/38274660/4698501
                【解决方案17】:

                Swift 4+

                let paragraphStyle = NSMutableParagraphStyle()
                paragraphStyle.firstLineHeadIndent = 10
                
                // Swift 4.2++
                label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
                
                // Swift 4.1--
                label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedStringKey.paragraphStyle: paragraphStyle])
                

                【讨论】:

                  【解决方案18】:

                  根据 Swift 4.2(Xcode 10 beta 6)“UIEdgeInsetsInsetRect”被弃用。 我也将这个类声明为 public 以使其更有用。

                  public class UIPaddedLabel: UILabel {
                  
                      @IBInspectable var topInset: CGFloat = 5.0
                      @IBInspectable var bottomInset: CGFloat = 5.0
                      @IBInspectable var leftInset: CGFloat = 7.0
                      @IBInspectable var rightInset: CGFloat = 7.0
                  
                      public override func drawText(in rect: CGRect) {
                          let insets = UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
                          super.drawText(in: rect.inset(by: insets))
                      }
                  
                      public override var intrinsicContentSize: CGSize {
                          let size = super.intrinsicContentSize
                          return CGSize(width: size.width + leftInset + rightInset,
                                        height: size.height + topInset + bottomInset)
                      }
                  
                      public override func sizeToFit() {
                          super.sizeThatFits(intrinsicContentSize)
                      }
                  }
                  

                  【讨论】:

                  • 效果很好。但是我尝试在 CollectionViewCell 中使用它,并且在重用后它不能很好地调整大小(sizeToFit 和 layoutIfNeeded 之后的事件)。任何 id 如何调整它的大小?
                  • 我确实更新了 sizeToFit() 以使其与可重用视图一起使用
                  • sizeToFit() 应该公开为:“重写实例方法必须与其封闭类型一样可访问”
                  【解决方案19】:

                  如果您不想或不需要在 Storyboard 中使用 @IBInspectable / @IBDesignable UILabel(我认为它们的渲染速度太慢了),那么使用 UIEdgeInsets 而不是 4 个不同的 CGFloats 会更干净。

                  Swift 4.2 的代码示例:

                  class UIPaddedLabel: UILabel {
                      var padding = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
                  
                      public override func drawText(in rect: CGRect) {
                          super.drawText(in: rect.inset(by: padding))
                      }
                  
                      public override var intrinsicContentSize: CGSize {
                          let size = super.intrinsicContentSize
                          return CGSize(width: size.width + padding.left + padding.right,
                                        height: size.height + padding.top + padding.bottom)
                      }
                  }
                  

                  【讨论】:

                    【解决方案20】:

                    如果你想在 textRect 周围添加 2px 的内边距,只需这样做:

                    let insets = UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2)
                    label.frame = UIEdgeInsetsInsetRect(textRect, insets)
                    

                    【讨论】:

                    • 什么是textRect? label.frame?
                    【解决方案21】:

                    只需使用一个 UIButton,它已经内置。关闭所有额外的按钮功能,您就有了一个可以设置边缘 instets 的标签。

                    let button = UIButton()
                    button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
                    button.setTitle("title", for: .normal)
                    button.tintColor = .white // this will be the textColor
                    button.isUserInteractionEnabled = false
                    

                    【讨论】:

                    • 嘿,这是一个很棒的提示!无需扩展! :-D
                    • 设置isUserInteractionEnabled = false 很方便禁用它。
                    • 很棒的提示...我宁愿这样做也不愿进行扩展。
                    • 不错的提示,最大的优点是它也可以在 Interface Builder 中完成
                    • 无子类化等的最佳解决方案
                    【解决方案22】:

                    没有故事板:

                    class PaddingLabel: UILabel {
                    
                        var topInset: CGFloat
                        var bottomInset: CGFloat
                        var leftInset: CGFloat
                        var rightInset: CGFloat
                    
                        required init(withInsets top: CGFloat, _ bottom: CGFloat,_ left: CGFloat,_ right: CGFloat) {
                            self.topInset = top
                            self.bottomInset = bottom
                            self.leftInset = left
                            self.rightInset = right
                            super.init(frame: CGRect.zero)
                        }
                    
                        required init?(coder aDecoder: NSCoder) {
                            fatalError("init(coder:) has not been implemented")
                        }
                    
                        override func drawText(in rect: CGRect) {
                            let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
                            super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
                        }
                    
                        override var intrinsicContentSize: CGSize {
                            get {
                                var contentSize = super.intrinsicContentSize
                                contentSize.height += topInset + bottomInset
                                contentSize.width += leftInset + rightInset
                                return contentSize
                            }
                        }
                    }
                    

                    用法:

                    let label = PaddingLabel(8, 8, 16, 16)
                    label.font = .boldSystemFont(ofSize: 16)
                    label.text = "Hello World"
                    label.backgroundColor = .black
                    label.textColor = .white
                    label.textAlignment = .center
                    label.layer.cornerRadius = 8
                    label.clipsToBounds = true
                    label.sizeToFit()
                    
                    view.addSubview(label)
                    

                    结果:

                    【讨论】:

                    • 有效,但你知道如何让它接受多行吗?只需使用“PaddingLabel(withInsets: 8, 8, 16, 16)”更改 init
                    【解决方案23】:

                    在 Swift 3 中

                    最好最简单的方法

                    class UILabelPadded: UILabel {
                         override func drawText(in rect: CGRect) {
                         let insets = UIEdgeInsets.init(top: 0, left: 5, bottom: 0, right: 5)
                         super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
                        }
                    
                    }
                    

                    【讨论】:

                    • 请问您如何将此代码用于您的 UILabel?
                    • 嗨@David_2877 这是一个类,只需将这个类添加到您的项目中,当您想要 UILabel 时使用 UILabelPadded。如果您需要更多帮助,请告诉我。
                    【解决方案24】:

                    另一个没有子类化的选择是:

                    1. 设置标签text
                    2. sizeToFit()
                    3. 然后稍微增加标签高度以模拟填充

                      label.text = "someText"
                      label.textAlignment = .center    
                      label.sizeToFit()  
                      label.frame = CGRect( x: label.frame.x, y: label.frame.y,width:  label.frame.width + 20,height: label.frame.height + 8)
                      

                    【讨论】:

                    • 令人惊讶的是,这就是我所需要的,只是稍微修改了一下:label.frame = CGRect( x: label.frame.origin.x - 10, y: label.frame.origin.y - 4, width: label.frame.width + 20,height: label.frame.height + 8) -10 和 -4 用于中心化
                    【解决方案25】:

                    一种实用的解决方案是添加与主标签相同高度和颜色的空白标签。将主标签的前导/尾随空间设置为零,对齐垂直中心,并将宽度设置为所需的边距。

                    【讨论】:

                      【解决方案26】:

                      与其他答案类似,但使用 func 类来动态设置填充:

                      class UILabelExtendedView: UILabel
                      {
                          var topInset: CGFloat = 4.0
                          var bottomInset: CGFloat = 4.0
                          var leftInset: CGFloat = 8.0
                          var rightInset: CGFloat = 8.0
                      
                          override func drawText(in rect: CGRect)
                          {
                              let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
                              super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
                          }
                      
                          override public var intrinsicContentSize: CGSize
                          {
                              var contentSize = super.intrinsicContentSize
                              contentSize.height += topInset + bottomInset
                              contentSize.width += leftInset + rightInset
                              return contentSize
                          }
                      
                          func setPadding(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat){
                              self.topInset = top
                              self.bottomInset = bottom
                              self.leftInset = left
                              self.rightInset = right
                              let insets: UIEdgeInsets = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
                              super.drawText(in: UIEdgeInsetsInsetRect(self.frame, insets))
                          }
                      }
                      

                      【讨论】:

                        【解决方案27】:

                        只需使用自动布局:

                        let paddedWidth = myLabel.intrinsicContentSize.width + 2 * padding
                        myLabel.widthAnchor.constraint(equalToConstant: paddedWidth).isActive = true
                        

                        完成。

                        【讨论】:

                        • 你也可以对高度做同样的事情。
                        【解决方案28】:

                        Swift 3、iOS10 解决方案:

                        open class UIInsetLabel: UILabel {
                        
                            open var insets : UIEdgeInsets = UIEdgeInsets() {
                                didSet {
                                    super.invalidateIntrinsicContentSize()
                                }
                            }
                        
                            open override var intrinsicContentSize: CGSize {
                                var size = super.intrinsicContentSize
                                size.width += insets.left + insets.right
                                size.height += insets.top + insets.bottom
                                return size
                            }
                        
                            override open func drawText(in rect: CGRect) {
                                return super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
                            }
                        }
                        

                        【讨论】:

                          【解决方案29】:

                          带有实现示例的 Swift 3 代码

                          class UIMarginLabel: UILabel {
                          
                              var topInset:       CGFloat = 0
                              var rightInset:     CGFloat = 0
                              var bottomInset:    CGFloat = 0
                              var leftInset:      CGFloat = 0
                          
                              override func drawText(in rect: CGRect) {
                                  let insets: UIEdgeInsets = UIEdgeInsets(top: self.topInset, left: self.leftInset, bottom: self.bottomInset, right: self.rightInset)
                                  self.setNeedsLayout()
                                  return super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
                              }
                          }
                          
                          class LabelVC: UIViewController {
                          
                              //Outlets
                              @IBOutlet weak var labelWithMargin: UIMarginLabel!
                          
                              override func viewDidLoad() {
                                  super.viewDidLoad()
                          
                                  //Label settings.
                                  labelWithMargin.leftInset = 10
                                  view.layoutIfNeeded()
                              }
                          }
                          

                          不要忘记在故事板标签对象中添加类名 UIMarginLabel。 快乐编码!

                          【讨论】:

                            【解决方案30】:

                            斯威夫特 3

                            import UIKit
                            
                            class PaddingLabel: UILabel {
                            
                               @IBInspectable var topInset: CGFloat = 5.0
                               @IBInspectable var bottomInset: CGFloat = 5.0
                               @IBInspectable var leftInset: CGFloat = 5.0
                               @IBInspectable var rightInset: CGFloat = 5.0
                            
                               override func drawText(in rect: CGRect) {
                                  let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
                                  super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
                               }
                            
                               override var intrinsicContentSize: CGSize {
                                  get {
                                     var contentSize = super.intrinsicContentSize
                                     contentSize.height += topInset + bottomInset
                                     contentSize.width += leftInset + rightInset
                                     return contentSize
                                  }
                               }
                            }
                            

                            【讨论】:

                            • 只是一点评论:在身份检查器(自定义类)中将此类设置为标签,并在属性检查器中使用名为标签填充的新属性。低于 5 的填充也是无效的
                            • 这并不总是适用于多行标签,因为当标签计算其高度时,它假定填充为零。
                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2023-03-24
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多