为UILabel添加空格/填充

我有一个UILabel,我想在顶部和底部添加空格。在限制最小高度的情况下,我将其修改为:

Enter image description here

为了做到这一点,我使用了:

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

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

Enter image description here

328190 次浏览

只需要使用UIView作为父视图,并为带有自动布局的标签定义固定的边距。

如果你想坚持使用UILabel,不子类化它,芒迪给了你一个明确的解决方案

如果你想避免用UIView来包装UILabel,你可以使用UITextView来支持UIEdgeInsets (padding)或者子类UILabel来支持UIEdgeInsets。

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

textView.textContainerInset = UIEdgeInsetsMake(10, 0, 10, 0);
另外,如果你子类化UILabel,这种方法的一个例子是覆盖drawTextInRect方法
(objective - c) < / p >
- (void)drawTextInRect:(CGRect)uiLabelRect {
UIEdgeInsets myLabelInsets = {10, 0, 10, 0};
[super drawTextInRect:UIEdgeInsetsInsetRect(uiLabelRect, myLabelInsets)];
}

您还可以为您的新子类UILabel提供带有insets变量的TOP, LEFT, BOTTOM和RIGHT。

一个示例代码可以是:

In .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的要点。如果这些方法都不起作用,你可以尝试一下。

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

简单的方法

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


}

我已经在斯威夫特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'

enter image description here

你可以从IB正确地做到:

  1. 将文本更改为带属性

 attributetext

  1. 使用"…"进入下拉列表

enter image description here

  1. 你会看到一些填充属性的行,段落和文本变化缩进第一行或任何你想要的

enter image description here

  1. 检查结果

enter image description here

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

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

或者将Storyboard上的自定义UILabel类关联为Label的类。

简单的填充:

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


}

我在公认的答案中略加修改。有一个问题,当leftInsetrightInset增加时,一部分文本会消失,b/c标签的宽度会变窄,但高度不会像图中那样增加:

padding label with wrong intrinsic content size

要解决这个问题,你需要重新计算文本的高度,如下所示:

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

和结果:

padding label with right intrinsic content size

我希望能帮助一些和我处境相同的人。

简单填充(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)))
}


}

斯威夫特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
}
}
}

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()
}
}
不要忘记在storyboard label对象中添加类名UIMarginLabel。 编码快乐!< / p >

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

只需使用自动布局:

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

完成了。

类似于其他答案,但使用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. 设置标签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)
    

斯威夫特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
}
}

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


}

没有故事板:

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)

结果:

.

只需要使用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

如果你想在textRect周围添加2px的填充,只需要这样做:

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

描摹的回答的详细阐述。

例如,在UIView中嵌入一个标签,并通过自动布局强制填充。例子:

It looks like a padding UILabel

概述:

  1. 创建UIView ("panel"),并设置其外观。

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

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

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

细节:

  1. 创建面板视图。

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

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

    let label = UILabel() panel.addSubview(标签)< / p >

  3. 在标签和面板的边缘之间添加约束。这迫使面板与标签保持一定距离。例如,“padding"。

编辑:手工完成所有这些是超级乏味、冗长和容易出错的。我建议你从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
  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

As per 斯威夫特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)
}
}

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

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

如果你不想或需要在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)
}
}

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

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

斯威夫特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])

斯威夫特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)

objective - c

基于大乐的回答,它在界面构建器可设计对象中实现了该特性,下面是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。

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

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

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

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

2. 必须重写drawText与新的更小的大小

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

在下面的典型示例中,文本单元位于表视图、堆栈视图或类似结构中,这给了它固定宽度。在这个例子中,我们想要填充为60,20,20,24。

因此,我们把“存在”;intrinsicContentSize ,然后在height上加80

重复一遍……

你必须从字面上“get”;“到目前为止”计算的高度;改变该值。

我觉得这个过程很混乱,但是,这就是它的工作原理。对我来说,苹果应该公开一个名为“初步高度计算”的调用。

其次,我们必须实际使用textrect# forBounds调用与我们新的更小的大小

因此,在textrect# forBounds中,我们< >强第一个< / >强使大小变小,< >强然后< / >强调用super。

警报!你必须调用超级,而不是之前!

如果你仔细研究本页上的所有尝试和讨论,这就是问题所在。

注意一些解决方案“似乎通常有效”。这确实是确切的原因——令人困惑的是,你必须“在之后打电话给超级”,而不是在之前。

如果你以错误的顺序调用super,它通常会工作,但对于特定的文本长度无效

下面是一个“不正确地做超级第一”的直观例子:

enter image description here

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

修复:

只有现在textrect# forBounds引擎知道如何正确地进行计算:

enter image description here

终于!

同样,在这个例子中,UILabel是在宽度固定的典型情况下使用的。因此在intrinsicContentSize中,我们必须“添加”;我们想要的整体高度。(你不需要"添加"以任何方式的宽度,这将是没有意义的,因为它是固定的。)

然后在textrect# forBounds中,你会得到“建议到目前为止”的边界;通过自动布局,你< >强减去< / >强你的页边距,and only then再次调用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在错误的地方”;。你必须在intrinsicContentSize中强制size更大,然后在textrect# forBounds中你< >强必须首先< / >强缩小第一个建议的边界< >强然后< / >强调用super。

总结:你必须“调用超级最后的"在textRect # forBounds

这就是秘诀。

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

如果你只寻找左右填充,你可以简单地在文本前后添加空白:

titleLabel.text = " \(categoryName) "

enter image description here

enter image description here

enter image description here

斯威夫特5

在layoutSubviews()方法上,更改UILabel的插入,如下所示。

  override func layoutSubviews() {
super.layoutSubviews()


label.frame = label.frame.inset(by: UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10))
}

我的解决方案类似于人们回答的问题,但添加了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
}
}

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

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

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.

严格适用于单行标签:(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
}
}

enter image description here

注意100/50上下的填充。

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

这样你就不必考虑标题上方/下方的空格,用户名等等——你只需要把它放到堆栈视图或其他任何情况下。

当然,当设计师想要调整时,你也可以在任何地方同时更改这两个值。

提醒:如果你想真正填充一个UILabel 所以不管文本行数多少,它都能完美运行,动态调整单元格,等等等等,这是非常复杂的。正确答案在上面“适用于所有情况的完整而正确的解决方案”下面。

如果你想使用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))
}
}