Thêm không gian / phần đệm vào UILabel


187

Tôi có một UILabelnơi tôi muốn thêm không gian ở trên cùng và ở dưới cùng. Với chiều cao tối thiểu trong giới hạn, tôi đã sửa đổi nó thành:

nhập mô tả hình ảnh ở đây

EDIT: Để làm điều này tôi đã sử dụng:

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

    } 

Nhưng tôi phải tìm một phương pháp khác vì nếu tôi viết nhiều hơn hai dòng thì vấn đề là như nhau:

nhập mô tả hình ảnh ở đây


1
Bản sao có thể có của lề văn bản UILabel
gblazex

Cuối cùng chúng tôi đã tìm ra chính xác làm thế nào để làm điều này đúng cách, trong tất cả các trường hợp động, như là một sự thay thế thả xuống hoàn hảo cho UILabel mà không cần phải bố trí lại hoặc bất kỳ vấn đề nào khác. HÃY THỬ. stackoverflow.com/a/58876988/294884
Fattie

Câu trả lời:


122

Nếu bạn muốn gắn bó với UILabel, mà không cần phân lớp nó, Mundi đã cho bạn một giải pháp rõ ràng.

Nếu thay vào đó, bạn sẽ sẵn sàng tránh bọc UILabel bằng UIView, bạn có thể sử dụng UITextView để cho phép sử dụng UIEdgeInsets (đệm) hoặc UILabel lớp con để hỗ trợ UIEdgeInsets.

Sử dụng UITextView sẽ chỉ cần cung cấp các phần tử (OBJ-C):

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

Thay vào đó, nếu bạn phân lớp UILabel , một ví dụ cho phương pháp này sẽ ghi đè phương thức drawTextInRect
(OBJ-C)

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

Bạn cũng có thể cung cấp UILabel phân lớp mới của mình với các biến chèn cho TOP, LEFT, BOTTOM và RIGHT.

Một mã ví dụ có thể là:

Trong .h (OBJ-C)

float topInset, leftInset,bottomInset, rightInset;

Trong .m (OBJ-C)

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

EDIT # 1:

Từ những gì tôi đã thấy, có vẻ như bạn phải ghi đè lên IntinsicContentSize của UILabel khi phân lớp nó.

Vì vậy, bạn nên ghi đè lên intinsicContentSize như:

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

Và thêm phương pháp sau để chỉnh sửa phần trong của bạn, thay vì chỉnh sửa riêng lẻ:

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

Nó sẽ cập nhật kích thước của UILabel của bạn để phù hợp với các miếng đệm cạnh và bao gồm sự cần thiết đa dòng mà bạn đã đề cập.

Chỉnh sửa # 2

Sau khi tìm kiếm một chút, tôi đã tìm thấy Gist này với IPInsetLabel. Nếu không có giải pháp nào trong số đó hoạt động, bạn có thể dùng thử.

Chỉnh sửa # 3

Có một câu hỏi tương tự (trùng lặp) về vấn đề này.
Để biết danh sách đầy đủ các giải pháp khả dụng, hãy xem câu trả lời sau: lề văn bản UILabel


Xin lỗi nhưng tôi đã sử dụng rất nhiều: `ghi đè func drawTextInRect (orth: CGRect) {var insets: UIEdgeInsets = UIEdgeInsets (top: 0.0, left: 10.0, bottom: 0.0, right: 10.0) super.drawTextInRect (UIEd ))} `nó không hoạt động vì kết quả là như nhau, không hoạt động linh hoạt ..
Annachiara

Bạn đã thử với UITextView thay vì UILabel chưa? Hay bạn thực sự cần sử dụng một UILabel?
nunofmendes

@Annachiara kiểm tra chỉnh sửa tôi đã thực hiện. Xem nếu nó hoạt động.
nunofmendes

Đồng ý. Nó đã làm việc văn bản? Xin lỗi vì đã không viết bằng Swift nhưng tôi vẫn ở chế độ Obj-C. Mục tiêu của tôi với mã đó là giúp bạn đạt được một số kết luận. Hy vọng nó đã làm.
nunofmendes

1
Sử dụng TextView và một số cài đặt bảng phân cảnh và self.textview.textContainerInset = UIEdgeInsetsMake (0, 10, 10, 10); Cuối cùng nó cũng hoạt động! Cảm ơn !
Annachiara

208

Tôi đã thử với nó trên Swift 4.2 , hy vọng nó hoạt động cho bạn!

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

Hoặc bạn có thể sử dụng CocoaPods tại đây https://github.com/levantAJ/PaddingLabel

pod 'PaddingLabel', '1.2'

nhập mô tả hình ảnh ở đây


6
Độ rộng uilabel không thay đổi, khiến văn bản trở thành "..."
neobie

1
@Tai Le, cảm ơn vì đã chia sẻ, tôi đã sử dụng nó trong phần xem bàn, tôi không biết tại sao nó lại cắt bớt văn bản, vd. sinh viên trở thành studen,
vishwa.deepak

1
@Tim có lẽ bạn muốn sử dụngmin
ielyamani

4
Một lời cảnh báo ở đây. Tôi đã sử dụng giải pháp này trong một lớp con UILabel. Khi sử dụng các nhãn này ở chế độ đa dòng, trong UIStackView dọc, có một vấn đề. Đôi khi, nhãn dường như bao bọc văn bản mà không định cỡ chính xác cho nhãn - vì vậy một từ hoặc 2 kết thúc bị thiếu từ cuối chuỗi. Tôi không có giải pháp ngay bây giờ. Tôi sẽ viết nó lên đây nếu tôi làm một cái. Tôi đã dành hàng giờ chọc vào vấn đề này, trước khi chứng minh nó ở đây.
Darren Black

1
Để làm cho nó hoạt động trong những tình huống đó, bạn cần ghi đè "setBound" và đặt self.preferredMaxLayoutWidth về giới hạn, trừ phần bên trái và bên phải của bạn
Arnaud Barisain-Monrose

82

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

chỉ cần một nhận xét nhỏ: đặt lớp này thành nhãn trong trình kiểm tra danh tính (lớp tùy chỉnh) và sử dụng thuộc tính mới trong trình kiểm tra thuộc tính có tên là đệm nhãn. cũng dưới 5 phần đệm là vô hiệu
iman kazemayni

3
Điều này không phải lúc nào cũng hoạt động chính xác với nhãn multiline, bởi vì khi nhãn tính chiều cao của nó, nó giả sử không có đệm.
fhucho

76

Bạn có thể làm điều đó đúng từ IB:

  1. thay đổi văn bản để quy kết

văn bản quy kết

  1. đi đến danh sách thả xuống với "..."

nhập mô tả hình ảnh ở đây

  1. bạn sẽ thấy một số thuộc tính đệm cho các dòng, đoạn và văn bản thay đổi thụt dòng đầu tiên hoặc bất cứ điều gì bạn muốn

nhập mô tả hình ảnh ở đây

  1. kiểm tra kết quả

nhập mô tả hình ảnh ở đây


1
Trong bảng phân cảnh của tôi, tôi có thể thấy văn bản thay đổi nhưng khi tôi chạy ứng dụng. Văn bản không hiển thị sự thay đổi ... T_T .. nhãn của tôi nằm trong một ô tùy chỉnh, có vấn đề gì không?
A. Trejo

1
@ A.Trejo Có thể là ô tùy chỉnh của bạn đặt thuộc tính nhãn trong thời gian chạy.
Pierre-Yves Guillemet

1
Thay đổi có thể xuất hiện trên bảng phân cảnh nhưng khi bạn chạy ứng dụng thì không có thay đổi nào.
Rhenz

4
Điều này không áp dụng khi bạn đặt văn bản theo chương trình.
Nij

1
Đây không phải là câu trả lời. Bạn chỉ có quyền kiểm soát thụt dòng đầu tiên, bot không phải là phần đệm theo mọi hướng.
rommex

49

Chuyển 4

Giải pháp dễ sử dụng, có sẵn cho tất cả trẻ em UILabel trong dự án.

Thí dụ:

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

Mở rộng 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
vui lòng giải thích ngắn gọn câu trả lời của bạn và không chỉ đăng mã.
lmiguelvargasf

4
Tiện ích mở rộng của bạn hủy giá trị 0 thành numberOfLines
Antoine Bodart

Điều này thật tuyệt, nhưng tôi gặp vấn đề với nhiều dòng, thậm chí tôi nghĩ rằng tôi thêm số dòng 2 hoặc để lại ở 0, nó luôn hiển thị một dòng. Bạn biết làm thế nào để giải quyết nó?
Mago Nicolas Palacios

1
@AntoineBodart bạn đã quản lý để giải quyết vấn đề numberOfLines?
Mago Nicolas Palacios

@ AntoineBodart, @Mago Nicolas Palacios - Đã được giải quyết!
iEvgen Podkorytov

47

Chỉ cần sử dụng UIViewnhư một giám sát và xác định lề cố định cho nhãn với bố cục tự động.


1
drawTextInRect chỉ hoạt động cho 1 dòng, intrinsicContentSizekhông hoạt động với đệm ngang. Bọc UILabel bên trong UIView là cách tốt để đi
onmyway133

8
Nếu bạn đang ở IB, bây giờ là thời gian để nhớ menu Trình chỉnh sửa -> Nhúng vào -> Xem. Chỉ cần chọn UILabel của bạn trước :)
Graham Perks

46

Chỉ cần sử dụng một UIButton, nó đã được tích hợp sẵn. Tắt tất cả các tính năng của nút phụ và bạn có một nhãn mà bạn có thể đặt instets cạnh.

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
Này đây là một mẹo tuyệt vời! Không cần gia hạn! :-D
Felipe Ferri

2
Cài đặt isUserInteractionEnabled = falselà tiện dụng để vô hiệu hóa nó.
mxcl

Mẹo hay ... Tôi muốn làm điều này hơn là mở rộng.
Ross

1
Mẹo hay, với lợi thế lớn mà nó cũng có thể được thực hiện trong Trình tạo giao diện
Ely

1
Giải pháp tốt nhất mà không cần phân lớp và vv
MeGaPk

16

Không có Storyboard:

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

Sử dụng:

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)

Kết quả:


Hoạt động, nhưng bạn có biết làm thế nào để làm cho nó chấp nhận nhiều dòng? Chỉ cần thay đổi init bằng "PaddingLabel (withInsets: 8, 8, 16, 16)"
Camilo Ortegón

16

Đệm a UILabel, giải pháp đầy đủ. Cập nhật năm 2020.

Hóa ra có ba điều phải được thực hiện.

1. Phải gọi textRect # forBound với kích thước mới nhỏ hơn

2. Phải ghi đè drawText với kích thước nhỏ hơn mới

3. Nếu một ô có kích thước động, phải điều chỉnh intinsicContentSize

Trong ví dụ điển hình dưới đây, đơn vị văn bản nằm trong chế độ xem bảng, chế độ xem ngăn xếp hoặc cấu trúc tương tự, cung cấp cho nó một chiều rộng cố định . Trong ví dụ này, chúng tôi muốn phần đệm là 60,20,20,24.

Vì vậy, chúng tôi lấy "intinsicContentSize" hiện có và thực sự thêm 80 vào chiều cao .

Lặp lại ...

Bạn phải "lấy" chiều cao tính theo "cho đến nay" của động cơ và thay đổi giá trị đó.

Tôi thấy quá trình đó khó hiểu, nhưng, đó là cách nó hoạt động. Đối với tôi, Apple nên trưng ra một cuộc gọi có tên như "tính toán chiều cao sơ ​​bộ".

Thứ hai, chúng ta phải thực sự sử dụng lệnh gọi textRect # forBound với kích thước nhỏ hơn mới của chúng tôi .

Vì vậy, trong textRect # forBound, trước tiên chúng ta làm cho kích thước nhỏ hơn và sau đó gọi siêu.

Thông báo! Bạn phải gọi siêu sau chứ không phải trước!

Nếu bạn điều tra cẩn thận tất cả các nỗ lực và thảo luận trên trang này, đó là vấn đề chính xác. Lưu ý một số giải pháp "dường như gần như hoạt động" nhưng sau đó sẽ có người báo cáo rằng trong một số tình huống nhất định, nó sẽ không hoạt động. Đây thực sự là lý do chính xác - thật khó hiểu là bạn phải "gọi siêu sau", chứ không phải trước đó.

Vì vậy, nếu bạn gọi siêu nó là "theo thứ tự sai", nó thường hoạt động, nhưng không cho độ dài văn bản cụ thể .

Dưới đây là một ví dụ trực quan chính xác về "làm sai trước tiên":

nhập mô tả hình ảnh ở đây

Lưu ý rằng các lề 60,20,20,24 là chính xác NHƯNG tính toán kích thước thực sự sai, bởi vì nó được thực hiện với mẫu "siêu đầu tiên" trong textRect # forBound.

Đã sửa:

Lưu ý rằng chỉ bây giờ công cụ textRect # forBound mới biết cách thực hiện phép tính đúng :

nhập mô tả hình ảnh ở đây

Cuối cùng!

Một lần nữa, trong ví dụ này, UILabel đang được sử dụng trong tình huống điển hình là độ rộng được cố định. Vì vậy, trong intinsicContentSize, chúng ta phải "thêm" chiều cao tổng thể thêm mà chúng ta muốn. (Bạn không cần phải "thêm" theo bất kỳ cách nào vào chiều rộng, điều đó sẽ vô nghĩa vì nó đã được sửa.)

Sau đó, trong textRect # forBound, bạn nhận được các giới hạn "được đề xuất cho đến nay" bằng cách tự động thanh toán, bạn trừ đi các lề của mình và chỉ sau đó gọi lại cho công cụ textRect # forBound, nghĩa là siêu, sẽ cho bạn kết quả.

Cuối cùng và đơn giản là trong drawText, bạn tất nhiên vẽ trong cùng một hộp nhỏ hơn.

Phù!

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
}

Một lần nữa. Lưu ý rằng các câu trả lời về điều này và QA khác "gần như" đúng là có vấn đề trong hình ảnh đầu tiên ở trên - "siêu ở sai vị trí" . Bạn phải buộc kích thước lớn hơn trong intinsicContentSize và sau đó trong textRect # forBound, trước tiên bạn phải thu nhỏ các giới hạn gợi ý đầu tiên và sau đó gọi siêu.

Tóm tắt: bạn phải "gọi siêu cuối " trong textRect # forBound

Đó là bí mật.

Lưu ý rằng bạn không cần và không cần phải gọi thêm không hợp lệ, sizeThatFits, cầnLayout hoặc bất kỳ cuộc gọi bắt buộc nào khác. Một giải pháp chính xác sẽ hoạt động đúng trong chu kỳ rút thăm tự động bình thường.


Tiền boa:

Nếu bạn đang làm việc với phông chữ đơn cách, đây là một mẹo hay: https://stackoverflow.com/a/59813420/294884


11

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

sử dụng:

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

WAIT - thực sự có một vấn đề tôi tìm thấy với điều này trong một số trường hợp. Trước đây đây là câu trả lời đúng nhất. Tôi đã đưa ra câu trả lời đúng dưới đây.
Fattie

Tôi đã bao gồm một hình ảnh trong câu trả lời của mình cho thấy vấn đề
Fattie

9

Mã Swift 3 với ví dụ triển khai

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

Đừng quên thêm tên lớp UIMarginLabel trong đối tượng nhãn bảng phân cảnh. Chúc mừng mã hóa!


8

Theo Swift 4.2 (Xcode 10 beta 6) "UIEdgeInsetsInsetRect" không được dùng nữa. Tôi cũng đã tuyên bố lớp học công khai để làm cho nó hữu ích hơn.

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

Nó hoạt động tốt. Nhưng tôi cố gắng sử dụng nó trong CollectionViewCell và nó không thay đổi kích thước tốt sau khi sử dụng lại (sự kiện sau sizeToFit và layout IfNeeded). Bất kỳ id làm thế nào để thay đổi kích thước nó?
Bogy

1
Tôi đã cập nhật sizeToFit () để làm cho nó hoạt động với chế độ xem có thể sử dụng lại
Bogy

sizeToFit()nên được công khai như sau: "Phương thức cá thể ghi đè phải có thể truy cập được như kiểu bao quanh của nó"
psyFi

7

Tôi chỉnh sửa một chút trong câu trả lời được chấp nhận. Có một vấn đề khi leftInsetrightInsettăng lên, một phần văn bản sẽ bị biến mất, b / c chiều rộng của nhãn sẽ bị thu hẹp nhưng chiều cao không tăng như hình:

nhãn đệm với kích thước nội dung sai

Để giải quyết vấn đề này, bạn cần tính lại chiều cao của văn bản như sau:

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

và kết quả:

nhãn đệm với kích thước nội dung bên phải

Tôi hy vọng sẽ giúp được một số người có cùng hoàn cảnh như tôi.


2
Nếu bạn dự định sử dụng Swift 3.0 , bạn phải thay đổi tên hàm, vì ngôn ngữ mới của Apple hoàn toàn phá vỡ định nghĩa func trước đó. Vì vậy, override func drawTextInRect(rect: CGRect)trở thành override func drawText(in rect: CGRect)override func intrinsicContentSize() -> CGSizetrở thành override var intrinsicContentSize : CGSize Thưởng thức!
DoK

tiếc là tôi đã không làm cho nó hoạt động. Tôi đã thử với mã số 5 của chúng tôi override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }
Nazmul Hasan

7

Chỉ cần sử dụng tự động thanh toán:

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

Làm xong.


Bạn cũng có thể làm tương tự với chiều cao.
ShadowToD

Cảm ơn vô cùng.
Mike Cheesne

7

Một tùy chọn khác mà không có phân lớp sẽ là:

  1. Đặt nhãn text
  2. sizeToFit()
  3. sau đó tăng chiều cao nhãn một chút để mô phỏng phần đệm

    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)

Đáng ngạc nhiên, đây là tất cả những gì tôi cần, chỉ cần sửa đổi một chút về điều này: 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 và -4 để tập trung hóa
Mofe Ejegi

6

Giải phá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))
    }
}

6

Trong Swift 3

cách tốt nhất và đơn giản

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

Phân lớp UILabel. (File-New-File- CocoaTouchClass-make Subgroup of 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
 }
}

Trên ViewContoder:

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

HOẶC Liên kết lớp UILabel tùy chỉnh trên Storyboard dưới dạng lớp của Nhãn.


Tôi sẽ bỏ phiếu nếu bạn thay đổi các giá trị được mã hóa cứng này thành các thuộc tính lớp, tôi đã sử dụng mã này.
Juan Boero

@ Juan: drawTextInRect là thuộc tính lớp mặc định của UILabel mà chúng tôi không thể ghi đè bằng mã. Cách thực hành tốt nhất để phân lớp UILabel và thêm thay đổi khung cần thiết. Nhưng dù sao, nó cũng thuận tiện như tính năng Kế thừa.
AG

Tuy nhiên, điều này đúng, ít nhất là với Swift 3, intinsicContentSize không phải là một chức năng mà là một thuộc tính, vì vậy nên "ghi đè var intinsicContentSize: CGFloat {}" thay vì "ghi đè func intinsicContentSize", chỉ là một ghi chú.
joey

4

Cũng giống như các câu trả lời khác nhưng sửa một lỗi. Khi label.widthđược điều khiển bởi bố cục tự động, đôi khi văn bản sẽ bị cắt.

@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

Đệm dễ dàng (Swift 3.0, Alvin George trả lời):

  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

Một chi tiết về câu trả lời của Mundi.

tức là nhúng nhãn vào phần UIViewđệm và thực thi phần đệm thông qua Bố cục tự động. Thí dụ:

trông giống như một UILabel độn

Tổng quat:

1) Tạo một UIView("bảng") và đặt giao diện của nó.

2) Tạo một UILabelvà thêm nó vào bảng điều khiển.

3) Thêm các ràng buộc để thực thi phần đệm.

4) Thêm bảng vào phân cấp xem của bạn, sau đó định vị bảng.

Chi tiết:

1) Tạo giao diện bảng điều khiển.

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

2) Tạo nhãn, thêm nó vào bảng điều khiển dưới dạng một khung nhìn phụ.

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

3) Thêm các ràng buộc giữa các cạnh của nhãn và bảng điều khiển. Điều này buộc bảng điều khiển giữ một khoảng cách từ nhãn. tức là "đệm"

Biên tập: làm tất cả điều này bằng tay là siêu tẻ nhạt, dài dòng và dễ bị lỗi. Tôi khuyên bạn nên chọn trình bao bọc Bố cục tự động từ github hoặc tự viết

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) Thêm bảng điều khiển vào hệ thống phân cấp xem của bạn và sau đó thêm các ràng buộc định vị. ví dụ: ôm phía bên phải của một bảngViewCell, như trong hình ảnh ví dụ.

Lưu ý: bạn chỉ cần thêm các ràng buộc vị trí, không phải ràng buộc về chiều: Bố cục tự động sẽ giải quyết bố cục dựa trên cả intrinsicContentSizenhãn và các ràng buộc được thêm trước đó.

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

Sử dụng mã này nếu bạn đang phải đối mặt với vấn đề cắt xén văn bản trong khi áp dụng phần đệm.

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

Cảm ơn bạn đã đăng bài, tôi đang tìm kiếm một giải pháp liên quan đến đệm + cắt tỉa. Dường như với tôi giải pháp của bạn phá vỡ nhãn.numberOfLines = 0 mà tôi cần. Bất kỳ cách giải quyết?
Don

1

Tương tự như các câu trả lời khác, nhưng với một lớp func để thiết lập phần đệm một cách ngẫu nhiên:

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

Một giải pháp thực tế là thêm các nhãn trống có cùng chiều cao và màu sắc với nhãn chính. Đặt không gian hàng đầu / dấu ở nhãn chính thành 0, căn chỉnh các tâm dọc và làm cho chiều rộng lề bạn muốn.


1

Nếu bạn muốn thêm phần đệm 2px xung quanh textRect, chỉ cần làm điều này:

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

TextRect là gì? nhãn.frame?
Gustavo Baiocchi Costa

1

Nếu bạn không muốn hoặc không cần sử dụng UILabel @IBInspectable / @IBDesignable trong Storyboard (tôi nghĩ rằng dù sao chúng cũng được hiển thị quá chậm), thì việc sử dụng UIEdgeInsets thay vì 4 CGFloats khác nhau sẽ tốt hơn.

Ví dụ mã cho 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

Mục tiêu-C

Dựa trên câu trả lời của Tai Le trên đó thực hiện tính năng bên trong IB Designable, đây là phiên bản Objective-C.

Đặt cái này trong YourLabel.h

@interface YourLabel : UILabel

@property IBInspectable CGFloat topInset;
@property IBInspectable CGFloat bottomInset;
@property IBInspectable CGFloat leftInset;
@property IBInspectable CGFloat rightInset;

@end

Và điều này sẽ đi trong 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

Sau đó, bạn có thể sửa đổi trực tiếp các phần tử YourLabel trong Trình tạo giao diện sau khi chỉ định lớp bên trong XIB hoặc bảng phân cảnh, giá trị mặc định của các phần tử bằng không.


0

Cách dễ dàng

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

Đệm dễ dàng:

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

    }

Phiên bản 3.0 trên trang dưới cùng
odemolliens

0

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])
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.