向UILabel添加空间/填充


186

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的理想替代品,而无需重新布局或其他任何问题。PHEW。 stackoverflow.com/a/58876988/294884
Fattie

Answers:


121

如果您想坚持使用UILabel而不对其进行子类化,则Mundi为您提供了明确的解决方案。

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

使用UITextView仅需要提供插图(OBJ-C):

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

或者,如果您将UILabel子类化,则此方法的一个示例将是重写drawTextInRect方法
(OBJ-C)

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

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

示例代码可能是:

在.h(OBJ-C)中

float topInset, leftInset,bottomInset, rightInset;

以.m(OBJ-C)

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

编辑#1:

从我所看到的情况来看,子类化时似乎必须重写UILabel的internalContentSize。

因此,您应该像下面那样覆盖internalContentSize

- (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的大小以匹配边缘插图,并覆盖您引用的多行必要性。

编辑#2

经过一番搜索后,我发现了带有IPInsetLabel的Gist。如果这些解决方案均无效,则可以尝试一下。

编辑#3

关于此事,存在类似的问题(重复)。
有关可用解决方案的完整列表,请参见以下答案: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) ))}`这是行不通的,因为结果是一样的,不工作动态..
Annachiara

您是否尝试过使用UITextView而不是UILabel?还是您真的需要使用UILabel?
nunofmendes 2014年

@Annachiara检查我所做的编辑。看看是否可行。
nunofmendes 2014年

好。它在textview中起作用了吗?抱歉,我没有用Swift编写代码,但是我仍然处于Obj-C模式。我使用该代码的目的是帮助您得出一些结论。希望它做到了。
nunofmendes 2014年

1
使用TextView和一些故事板设置以及self.textview.textContainerInset = UIEdgeInsetsMake(0,10,10,10); 终于成功了!谢谢 !
Annachiara 2014年

206

我已经在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'

在此处输入图片说明


6
uilabel的宽度没有变化,导致文本变为“ ...”
neobie

1
@Tai Le,感谢分享,我在表视图中使用了它,我不知道为什么它会修剪文本,例如。学生成为学生,
vishwa.deepak '18

1
@Tim也许您打算使用min
ielyamani

4
在此警告一下。我一直在UILabel子类中使用此解决方案。在多行模式下使用这些标签时,在垂直UIStackView中会出现问题。有时,标签似乎在包装文本时没有正确调整标签的大小-因此一个或两个单词最终从字符串末尾丢失。我现在没有解决方案。如果我做一个,我会写在这里。我花了几个小时来研究这个问题,然后证明它在这里。
达伦·布莱克

1
为了使其在这种情况下起作用,您需要覆盖“ setBounds”并将self.preferredMaxLayoutWidth设置为边界的宽度,减去您的左右插图
Arnaud Barisain-Monrose

82

迅捷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
      }
   }
}

只需一点评论:将此类设置为在身份检查器(自定义类)中标记,并在属性检查器中使用名为label padding的新属性。也低于5填充是无效的
iman kazemayni

3
这对于多行标签并不总是正确地起作用,因为当标签计算其高度时,它假定填充为零。
fhucho

76

您可以从IB正确地做到这一点:

  1. 将文本更改为归因

归因文本

  1. 用“ ...”转到下拉列表

在此处输入图片说明

  1. 您将看到行,段落和文本的某些填充属性,以缩进第一行或您想要的任何内容为准

在此处输入图片说明

  1. 检查结果

在此处输入图片说明


1
在我的情节提要中,我可以看到文本更改,但是当我运行该应用程序时。文字没有显示更改... T_T ..我的标签在自定义单元格内,有什么问题吗?
A. Trejo

1
@ A.Trejo可能是您的自定义单元格在运行时设置了label属性。
Pierre-Yves Guillemet '16

1
可能会在情节提要板上显示更改,但运行应用程序时没有任何更改。
Rhenz

4
当您以编程方式设置文本时,这不适用。
尼斯,

1
这不是答案。您只能控制第一行缩进,不能控制所有方向的填充。
rommex

49

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
    }
}

1
请简要说明您的答案,而不仅是邮编。
lmiguelvargasf

4
您的扩展程序取消了numberOfLines的0值
Antoine Bodart

这很好,但是我遇到多行问题,甚至以为我将第2行的数字相加或保留为0,它总是显示1。你知道如何解决吗?
Mago Nicolas Palacios

1
@AntoineBodart您设法解决了numberOfLines问题吗?
Mago Nicolas Palacios

@ AntoineBodart,@ Mago Nicolas Palacios-解决了!
iEvgen Podkorytov

47

只需将a UIView用作超级视图,然后使用自动布局即可为标签定义固定的边距。


1
drawTextInRect 仅适用于1行,intrinsicContentSize不适用于水平填充。将UILabel包裹在UIView中是一个很好的方法
onmyway133 2015年

8
如果您在IB,现在是时候记住菜单编辑器->嵌入->视图。只需先选择您的UILabel :)
Graham Perks

46

只需使用已经内置的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

2
嘿,这是一个很好的提示!无需扩展!:-D
费利佩·费里

2
设置isUserInteractionEnabled = false方便禁用它。
mxcl

很棒的提示...我宁愿这样做,也不愿扩展。
罗斯

1
不错的提示,它的最大优点是也可以在Interface Builder中完成
Ely

1
无需子类化等的最佳解决方案
MeGaPk '18

16

没有情节提要:

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
CamiloOrtegón17年

15

填充UILabel完整的解决方案。于2020年更新。

事实证明,必须完成三件事

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

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

3.如果单元格的大小是动态的,则必须调整internalContentSize

在下面的典型示例中,文本单元处于表格视图,堆栈视图或类似的构造中,从而为其提供了固定的宽度。在示例中,我们希望填充为60,20,20,24。

因此,我们采用“现有的” internalContentSize 并实际上将80加到height

重复 ...

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

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

其次,我们必须实际使用具有较小的新大小的textRect#forBounds调用。

因此,在textRect#forBounds中,我们首先减小尺寸,然后调用super。

警报!您必须在之后而不是之前致电super !

如果您仔细调查此页面上的所有尝试和讨论,那就是确切的问题。注意一些解决方案“似乎几乎可以工作”,但是有人会报告在某些情况下它不起作用。这确实是确切的原因-令人困惑的是,您必须“事后呼叫超级”,而不是之前。

因此,如果将super称为“顺序错误”,则通常可以使用,但对于某些特定的文本长度则无效

这是“不正确地做超级第一”的确切视觉示例:

在此处输入图片说明

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

固定:

注意,只有现在,textRect#forBounds引擎才知道如何正确进行计算

在此处输入图片说明

最后!

同样,在此示例中,UILabel在固定宽度的典型情况下使用。因此,在internalContentSize中,我们必须“添加”所需的整体额外高度。(您不需要以任何方式“添加”宽度,因为它是固定的,所以没有意义。)

然后在textRect#forBounds中,通过自动布局获得“建议的边界”,减去边距,然后再次调用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上“几乎”正确的答案在上面的第一张图片中遇到了问题- “超级位置错误”。您必须在internalContentSize中强制增大该大小,然后在textRect#forBounds中必须首先缩小第一个建议边界,然后调用super。

摘要:您必须在textRect#forBounds中“呼叫超级倒数

那是秘密。

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


小费:

如果您正在使用等宽字体,这里有个很好的提示:https : //stackoverflow.com/a/59813420/294884


11

迅捷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)

等待-在某些情况下,我实际上发现了一个问题。以前,这是最正确的答案。我在下面输入了正确的答案。
Fattie

我在回答中包括一张显示问题的图像
Fattie

9

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。编码愉快!


8

根据Swift 4.2(Xcode 10 beta 6),“ UIEdgeInsetsInsetRect”已弃用。我还宣布了该类的公开性,以使其更加有用。

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如何调整大小?
Bogy

1
我确实更新过sizeToFit()以使其与可重用视图一起使用
Bogy

sizeToFit()应该公开为:“覆盖实例方法必须与其封闭类型一样可访问”
psyFi

7

我在接受的答案中做了一些编辑。当leftInsetrightInset增加时出现问题,部分文本会消失,b / c标签的宽度会变窄,但高度不会增加,如图所示:

内部内容大小错误的填充标签

要解决此问题,您需要重新计算文本高度,如下所示:

@IBDesignable class PaddingLabel: UILabel {

  @IBInspectable var topInset: CGFloat = 20.0
  @IBInspectable var bottomInset: CGFloat = 20.0
  @IBInspectable var leftInset: CGFloat = 20.0
  @IBInspectable var rightInset: CGFloat = 20.0

  override func drawTextInRect(rect: CGRect) {
    let insets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
    super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))
  }

  override func intrinsicContentSize() -> CGSize {
    var intrinsicSuperViewContentSize = super.intrinsicContentSize()

    let textWidth = frame.size.width - (self.leftInset + self.rightInset)
    let newSize = self.text!.boundingRectWithSize(CGSizeMake(textWidth, CGFloat.max), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: [NSFontAttributeName: self.font], context: nil)
    intrinsicSuperViewContentSize.height = ceil(newSize.size.height) + self.topInset + self.bottomInset

    return intrinsicSuperViewContentSize
  }
}

结果:

具有正确的内在内容大小的填充标签

我希望能帮助一些和我一样的人。


2
如果您打算使用Swift 3.0,则必须更改函数名称,因为新的Apple语言完全破坏了以前的func定义。因此,override func drawTextInRect(rect: CGRect)成为override func drawText(in rect: CGRect)override func intrinsicContentSize() -> CGSize成为override var intrinsicContentSize : CGSize 享受!
DoK 2013年

不幸的是我没有使它工作。我尝试使用我们的代码迅速5 override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }
Nazmul Hasan

7

没有子类的另一种选择是:

  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用于集中化
Mofe Ejegi

6

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))
    }
}

6

只需使用自动布局:

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

做完了


您也可以对身高做同样的事情。
ShadeToD

6

在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))
    }

}

4

子类UILabel。(UILabel的File-New-File-CocoaTouchClass-make子类)。

//  sampleLabel.swift

import UIKit

class sampleLabel: UILabel {

 let topInset = CGFloat(5.0), bottomInset = CGFloat(5.0), leftInset = CGFloat(8.0), rightInset = CGFloat(8.0)

 override func drawTextInRect(rect: CGRect) {

  let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
  super.drawTextInRect(UIEdgeInsetsInsetRect(rect, insets))

 }
 override func intrinsicContentSize() -> CGSize {
  var intrinsicSuperViewContentSize = super.intrinsicContentSize()
  intrinsicSuperViewContentSize.height += topInset + bottomInset
  intrinsicSuperViewContentSize.width += leftInset + rightInset
  return intrinsicSuperViewContentSize
 }
}

在ViewController上:

override func viewDidLoad() {
  super.viewDidLoad()

  let labelName = sampleLabel(frame: CGRectMake(0, 100, 300, 25))
  labelName.text = "Sample Label"
  labelName.backgroundColor =  UIColor.grayColor()

  labelName.textColor = UIColor.redColor()
  labelName.shadowColor = UIColor.blackColor()
  labelName.font = UIFont(name: "HelveticaNeue", size: CGFloat(22))
  self.view.addSubview(labelName)
 }

或将故事板上的自定义UILabel类与Label的类相关联。


如果您将这些硬编码的值更改为类属性,我将投赞成票,我已经在使用此代码。
Juan Boero '16

@ Juan:drawTextInRect是UILabel的默认类属性,我们无法使用代码覆盖。继承UILabel并添加所需的框架更改的最佳实践。无论如何,作为继承功能很方便。
AG

这是正确的,但是至少从Swift 3开始,intrinsicContentSize并不是一个函数,而是一个属性,因此应为“ override var nativeContentSize:CGFloat {}”,而不是“ override func internalContentContentSize”,只是一个注释。
joey

4

就像其他答案一样,但修复错误。当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))
    }
}

2

轻松填充(Swift 3.0,Alvin George回答):

  class NewLabel: UILabel {

        override func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
                return self.bounds.insetBy(dx: CGFloat(15.0), dy: CGFloat(15.0))
        }

        override func draw(_ rect: CGRect) {
                super.drawText(in: self.bounds.insetBy(dx: CGFloat(5.0), dy: CGFloat(5.0)))
        }

  }

2

详细阐述Mundi的答案。

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

看起来像是填充的UILabel

概述:

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

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

3)添加约束以强制执行填充。

4)将面板添加到视图层次结构中,然后放置面板。

细节:

1)创建面板视图。

let panel = UIView()
panel.backgroundColor = .green
panel.layer.cornerRadius = 12

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

let label = UILabel()
panel.addSubview(label)

3)在标签和面板的边缘之间添加约束。这迫使面板与标签保持一定距离。即“填充”

社论:手动完成所有这些操作非常繁琐,冗长且容易出错。我建议您从github选择一个自动布局包装器,或者自己写一个

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

4)将面板添加到视图层次结构,然后添加定位约束。例如,如示例图片所示,拥抱tableViewCell的右侧。

注意:您只需要添加位置约束,而无需添加尺寸约束:“自动布局”将基于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

2

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

@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。任何解决方法?

1

与其他答案类似,但是具有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))
    }
}

1

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



1

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

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)
    }
}

1

物镜

基于大乐那里的答案,该答案在IB 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插图,这些插图的默认值为零。


0

简单的方法

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.view.addSubview(makeLabel("my title",x: 0, y: 100, w: 320, h: 30))
    }

    func makeLabel(title:String, x:CGFloat, y:CGFloat, w:CGFloat, h:CGFloat)->UILabel{
        var myLabel : UILabel = UILabel(frame: CGRectMake(x,y,w,h))
        myLabel.textAlignment = NSTextAlignment.Right

        // inser last char to right
        var titlePlus1char = "\(title)1"
        myLabel.text = titlePlus1char
        var titleSize:Int = count(titlePlus1char)-1

        myLabel.textColor = UIColor(red:1.0, green:1.0,blue:1.0,alpha:1.0)
        myLabel.backgroundColor = UIColor(red: 214/255, green: 167/255, blue: 0/255,alpha:1.0)


        // create myMutable String
        var myMutableString = NSMutableAttributedString()

        // create myMutable font
        myMutableString = NSMutableAttributedString(string: titlePlus1char, attributes: [NSFontAttributeName:UIFont(name: "HelveticaNeue", size: 20)!])

        // set margin size
        myMutableString.addAttribute(NSFontAttributeName, value: UIFont(name: "HelveticaNeue", size: 10)!, range: NSRange(location: titleSize,length: 1))

        // set last char to alpha 0
        myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor(red:1.0, green:1.0,blue:1.0,alpha:0), range: NSRange(location: titleSize,length: 1))

        myLabel.attributedText = myMutableString

        return myLabel
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}

0

易于填充:

import UIKit

    class NewLabel: UILabel {

        override func textRectForBounds(bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {

            return CGRectInset(self.bounds, CGFloat(15.0), CGFloat(15.0))
        }

        override func drawRect(rect: CGRect) {

            super.drawTextInRect(CGRectInset(self.bounds,CGFloat(5.0), CGFloat(5.0)))
        }

    }

底部页面上的3.0版本
odemolliens

0

迅捷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])
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.