Menambahkan spasi / padding ke UILabel

187

Saya punya tempat di UILabelmana saya ingin menambahkan ruang di bagian atas dan di bagian bawah. Dengan ketinggian minimum dalam batasan saya telah memodifikasinya menjadi:

masukkan deskripsi gambar di sini

EDIT: Untuk melakukan ini saya telah menggunakan:

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

    } 

Tetapi saya harus menemukan metode yang berbeda karena jika saya menulis lebih dari dua baris masalahnya sama:

masukkan deskripsi gambar di sini

Annachiara
sumber
1
Kemungkinan duplikat margin teks UILabel
gblazex
Kami akhirnya menemukan cara untuk melakukan ini dengan benar, dalam semua kasus yang dinamis, sebagai pengganti drop-in yang sempurna untuk UILabel tanpa perlu menata ulang atau masalah lainnya. PHEW. stackoverflow.com/a/58876988/294884
Fattie

Jawaban:

122

Jika Anda ingin tetap menggunakan UILabel, tanpa mensubclassingnya, Mundi telah memberi Anda solusi yang jelas.

Jika tidak, Anda ingin menghindari membungkus UILabel dengan UIView, Anda bisa menggunakan UITextView untuk mengaktifkan penggunaan UIEdgeInsets (padding) atau subkelas UILabel untuk mendukung UIEdgeInsets.

Menggunakan UITextView hanya perlu menyediakan insets (OBJ-C):

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

Alternatif, jika Anda subkelas UILabel , contoh untuk pendekatan ini akan menimpa metode drawTextInRect
(OBJ-C)

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

Anda juga bisa memberikan UILabel subclass baru Anda dengan variabel insets untuk TOP, LEFT, BOTTOM dan RIGHT.

Contoh kode dapat berupa:

In .h (OBJ-C)

float topInset, leftInset,bottomInset, rightInset;

Dalam .m (OBJ-C)

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

EDIT # 1:

Dari apa yang saya lihat, sepertinya Anda harus menimpa intrinsicContentSize dari UILabel saat mensubklasifikasikan.

Jadi Anda harus mengganti intrinsicContentSize seperti:

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

Dan tambahkan metode berikut untuk mengedit insets Anda, daripada mengeditnya satu per satu:

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

Ini akan memperbarui ukuran UILabel Anda agar sesuai dengan insets tepi dan mencakup kebutuhan multiline yang Anda maksud.

Edit # 2

Setelah mencari sedikit, saya menemukan Intisari ini dengan IPInsetLabel. Jika tidak ada solusi yang berhasil, Anda bisa mencobanya.

Edit # 3

Ada pertanyaan serupa (duplikat) tentang masalah ini.
Untuk daftar lengkap solusi yang tersedia, lihat jawaban ini: Margin teks UILabel

nunofmendes
sumber
Maaf tapi saya sudah pernah menggunakan: `override func drawTextInRect (rect: CGRect) {var insets: UIEdgeInsets = UIEdgeInsets (atas: 0.0, kiri: 10.0, bawah: 0.0, kanan: 10.0) super.drawTextInRect (UIEdgeInsetsInsetRect (rect, insets ))} `itu tidak berfungsi karena hasilnya sama, tidak bekerja secara dinamis ..
Annachiara
Apakah Anda mencoba dengan UITextView, bukan UILabel? Atau apakah Anda benar-benar perlu menggunakan UILabel?
nunofmendes
@Annachiara periksa hasil edit yang saya buat. Lihat apakah itu berfungsi.
nunofmendes
Baik. Apakah ini berfungsi dengan tampilan teks? Maaf karena tidak menulis dalam Swift tetapi saya masih dalam mode Obj-C. Tujuan saya dengan kode itu adalah untuk membantu Anda mencapai beberapa kesimpulan. Semoga berhasil.
nunofmendes
1
Menggunakan TextView dan beberapa pengaturan papan cerita dan self.textview.textContainerInset = UIEdgeInsetsMake (0, 10, 10, 10); Akhirnya berhasil! Terima kasih!
Annachiara
208

Saya sudah mencobanya dengan Swift 4.2 , semoga berhasil untuk Anda!

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

Atau Anda dapat menggunakan CocoaPods di sini https://github.com/levantAJ/PaddingLabel

pod 'PaddingLabel', '1.2'

masukkan deskripsi gambar di sini

Tai Le
sumber
6
Lebar uilabel tidak berubah, menyebabkan teks menjadi "..."
neobie
1
@Tai Le, terima kasih sudah berbagi, saya telah menggunakannya di tableview, saya tidak tahu mengapa melakukan triming pada teks, misalnya. siswa menjadi pelajar,
vishwa.deepak
1
@Tim mungkin Anda bermaksud menggunakanmin
ielyamani
4
Sebuah kata peringatan di sini. Saya telah menggunakan solusi ini dalam subkelas UILabel. Saat menggunakan label ini dalam mode multiline, dalam UIStackView vertikal, ada masalah. Kadang-kadang label tampaknya membungkus teks tanpa dengan benar mengukur label - jadi satu atau dua kata akhirnya hilang dari ujung string. Saya tidak punya solusi sekarang. Saya akan menuliskannya di sini jika saya membuatnya. Saya menghabiskan berjam-jam mencari-cari masalah ini, sebelum membuktikannya ada di sini.
Darren Black
1
Untuk membuatnya berfungsi dalam situasi itu, Anda perlu mengganti "setBounds" dan mengatur self.preferredMaxLayoutLebar lebar, dikurangi insets kiri dan kanan Anda
Arnaud Barisain-Monrose
82

Cepat 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
      }
   }
}
zombie
sumber
hanya sedikit komentar: atur kelas ini ke label di inspektur identitas (kelas kustom) dan gunakan atribut baru di inspektur atribut bernama label padding. juga di bawah 5 padding tidak berpengaruh
iman kazemayni
3
Ini tidak selalu bekerja dengan benar dengan label multiline, karena ketika label menghitung ketinggiannya, ia mengasumsikan nol padding.
fhucho
76

Anda dapat melakukannya dengan benar dari IB:

  1. ubah teks menjadi atribut

teks yang dikaitkan

  1. buka daftar dropdown dengan "..."

masukkan deskripsi gambar di sini

  1. Anda akan melihat beberapa properti padding untuk baris, paragraf dan teks mengubah indentasi baris pertama atau apa pun yang Anda inginkan

masukkan deskripsi gambar di sini

  1. periksa hasilnya

masukkan deskripsi gambar di sini

Pierre-Yves Guillemet
sumber
1
Di storyboard saya, saya bisa melihat perubahan teks tetapi ketika saya menjalankan aplikasi. Teks tidak menunjukkan perubahan ... T_T .. label saya ada di dalam sel khusus, apakah ada masalah?
A. Trejo
1
@ A.Trejo Mungkin sel kustom Anda menyetel properti label pada saat dijalankan.
Pierre-Yves Guillemet
1
Perubahan dapat muncul di storyboard tetapi ketika Anda menjalankan aplikasi tidak ada perubahan.
Rhenz
4
Ini tidak berlaku ketika Anda mengatur teks secara programatik.
Nij
1
Ini bukan jawabannya. Anda hanya memiliki kontrol atas indentasi baris pertama, bukan padding di semua arah.
rommex
49

SWIFT 4

Solusi yang mudah digunakan, tersedia untuk semua anak UILabel dalam proyek.

Contoh:

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

Ekstensi 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
    }
}
iEvgen Podkorytov
sumber
1
tolong jelaskan jawaban Anda secara singkat, dan jangan hanya mengirim kode.
lmiguelvargasf
4
Ekstensi Anda membatalkan nilai 0 ke numberOfLines
Antoine Bodart
Ini bagus, tetapi saya mengalami masalah dengan beberapa baris, bahkan berpikir saya menambahkan angka dari baris 2 atau meninggalkan 0, itu selalu menunjukkan satu. Anda tahu cara mengatasinya?
Mago Nicolas Palacios
1
@AntoineBodart apakah Anda berhasil memecahkan masalah numberOfLines?
Mago Nicolas Palacios
@ AntoineBodart, @Mago Nicolas Palacios - Dipecahkan!
iEvgen Podkorytov
47

Cukup gunakan UIViewsebagai superview dan tentukan margin tetap pada label dengan tata letak otomatis.

Mundi
sumber
1
drawTextInRect hanya berfungsi untuk 1 baris, intrinsicContentSizetidak bekerja dengan padding horizontal. Bungkus UILabel di dalam UIView adalah cara yang baik untuk pergi
onmyway133
8
Jika Anda berada di IB, sekarang adalah waktu untuk mengingat menu Editor -> Embed In -> View. Cukup pilih UILabel Anda terlebih dahulu :)
Graham Perks
46

Cukup gunakan UIButton, yang sudah ada di dalamnya. Matikan semua fitur tombol tambahan dan Anda memiliki label yang dapat Anda setel pada instet tepi.

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
Steve M
sumber
2
Hei ini tip yang bagus! Tidak perlu ekstensi! :-D
Felipe Ferri
2
Pengaturan isUserInteractionEnabled = falseberguna untuk menonaktifkannya.
mxcl
Tip bagus ... Saya lebih suka melakukan ini daripada pergi untuk perpanjangan.
Ross
1
Tip bagus, dengan keuntungan besar yang juga bisa dilakukan di Interface Builder
Ely
1
Solusi terbaik tanpa subklas dan lain
MeGaPk
16

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

Pemakaian:

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)

Hasil:

Alice Chan
sumber
Berhasil, tetapi apakah Anda tahu cara membuatnya menerima beberapa baris? Cukup ganti init dengan "PaddingLabel (withInsets: 8, 8, 16, 16)"
Camilo Ortegón
16

Padding a UILabel , solusi lengkap. Diperbarui untuk 2020.

Ternyata ada tiga hal yang harus dilakukan.

1. Harus memanggil textRect # forBounds dengan ukuran baru yang lebih kecil

2. Harus mengganti drawText dengan ukuran baru yang lebih kecil

3. Jika sel berukuran dinamis, harus menyesuaikan intrinsicContentSize

Dalam contoh khas di bawah ini, unit teks dalam tampilan tabel, tampilan tumpukan atau konstruksi serupa, yang memberikan lebar tetap . Dalam contoh ini kami ingin menambahkan 60,20,20,24.

Jadi, kita ambil intrinsicContentSize "yang ada" dan benar-benar menambahkan 80 ke ketinggian .

Mengulang ...

Anda harus benar-benar "menghitung" ketinggian yang dihitung "sejauh ini" oleh mesin, dan mengubah nilainya.

Saya menemukan proses itu membingungkan, tetapi, itulah cara kerjanya. Bagi saya, Apple harus mengekspos panggilan bernama "perhitungan tinggi pendahuluan".

Kedua, kita harus benar-benar menggunakan panggilan textRect # forBounds dengan ukuran baru yang lebih kecil .

Jadi dalam textRect # forBounds kita pertama membuat ukuran yang lebih kecil dan kemudian memanggil super.

Waspada! Anda harus menelepon super setelah , bukan sebelumnya!

Jika Anda hati-hati menyelidiki semua upaya dan diskusi di halaman ini, itulah masalah yang sebenarnya. Perhatikan beberapa solusi "tampaknya hampir berhasil" tetapi kemudian seseorang akan melaporkan bahwa dalam situasi tertentu itu tidak akan berhasil. Ini memang alasan yang tepat - membingungkan Anda harus "memanggil super sesudahnya", bukan sebelumnya.

Jadi, jika Anda memanggil super itu "dalam urutan yang salah", biasanya berfungsi, tetapi tidak untuk panjang teks tertentu tertentu .

Berikut ini adalah contoh visual yang tepat dari "salah melakukan super pertama":

masukkan deskripsi gambar di sini

Perhatikan bahwa margin 60,20,20,24 sudah benar TETAPI perhitungan ukurannya sebenarnya salah, karena dilakukan dengan pola "super first" di textRect # forBounds.

Tetap:

Perhatikan bahwa hanya sekarang mesin textRect # forBounds tahu cara melakukan perhitungan dengan benar :

masukkan deskripsi gambar di sini

Akhirnya!

Sekali lagi, dalam contoh ini, UILabel digunakan dalam situasi tipikal di mana lebar ditetapkan. Jadi dalam intrinsicContentSize kita harus "menambahkan" tinggi ekstra keseluruhan yang kita inginkan. (Anda tidak perlu "menambahkan" dengan cara apa pun ke lebar, itu akan menjadi tidak berarti karena sudah diperbaiki.)

Kemudian di textRect # forBounds Anda mendapatkan batasan "disarankan sejauh ini" dengan autolayout, Anda kurangi margin Anda, dan hanya dengan itu memanggil lagi ke mesin textRect # forBounds, yaitu di super, yang akan memberi Anda hasil.

Akhirnya dan cukup di drawText Anda tentu saja menggambar di kotak kecil yang sama.

Fiuh!

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
}

Sekali lagi. Perhatikan bahwa jawaban pada QA ini dan lainnya yang "hampir" benar mengalami masalah pada gambar pertama di atas - "super ada di tempat yang salah" . Anda harus memaksa ukuran lebih besar di intrinsicContentSize dan kemudian di textRect # forBounds Anda harus terlebih dahulu mengecilkan batas saran pertama dan kemudian memanggil super.

Ringkasan: Anda harus "memanggil super terakhir " di textRect # forBounds

Itulah rahasianya.

Perhatikan bahwa Anda tidak perlu dan tidak perlu memanggil tambahan yang tidak valid, sizeThatFits, needsLayout atau panggilan pemaksaan lainnya. Solusi yang benar harus bekerja dengan baik dalam siklus penarikan autolayout normal.


Tip:

Jika Anda bekerja dengan font monospace, inilah tip yang bagus: https://stackoverflow.com/a/59813420/294884

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

pemakaian:

let label = EdgeInsetLabel()
label.textInsets = UIEdgeInsets(top: 2, left: 6, bottom: 2, right: 6)
LE24
sumber
TUNGGU - sebenarnya ada masalah yang saya temukan dengan ini dalam beberapa kasus. Sebelumnya ini adalah jawaban yang paling benar. Saya telah memasukkan jawaban yang benar di bawah ini.
Fattie
Saya telah memasukkan gambar dalam jawaban saya yang menunjukkan masalah
Fattie
9

Kode Swift 3 dengan Contoh Implementasi

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

Jangan lupa untuk menambahkan nama kelas UIMarginLabel di objek label storyboard. Selamat Coding!

mriaz0011
sumber
8

Sesuai Swift 4.2 (Xcode 10 beta 6) "UIEdgeInsetsInsetRect" tidak digunakan lagi. Saya juga telah mengumumkan kelas publik untuk membuatnya lebih bermanfaat.

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)
    }
}
Stéphane de Luca
sumber
Ini bekerja dengan baik. Tapi saya mencoba menggunakannya di dalam CollectionViewCell dan tidak mengubah ukuran dengan baik setelah digunakan kembali (event after sizeToFit dan layoutIfNeeded). Adakah id cara mengubah ukurannya?
Bogy
1
Saya melakukan update sizeToFit () agar berfungsi dengan tampilan yang dapat digunakan kembali
Bogy
sizeToFit()harus dipublikasikan sebagai: "Metode instance utama harus dapat diakses seperti jenis terlampirnya"
psyFi
7

Saya mengedit sedikit jawaban yang diterima. Ada masalah ketika leftInsetdan rightInsetmeningkat, sebagian teks akan hilang, b / c lebar label akan dipersempit tetapi tingginya tidak bertambah seperti gambar:

label padding dengan ukuran konten intrinsik yang salah

Untuk mengatasi masalah ini, Anda perlu menghitung ulang ketinggian teks sebagai berikut:

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

dan hasil:

label padding dengan ukuran konten intrinsik yang tepat

Saya berharap dapat membantu beberapa orang dalam situasi yang sama dengan saya.

Quang Tran
sumber
2
Jika Anda berencana untuk menggunakan Swift 3.0 , Anda harus mengubah nama fungsi, karena bahasa Apple yang baru benar-benar merusak definisi fungsi sebelumnya. Jadi, override func drawTextInRect(rect: CGRect)jadilah override func drawText(in rect: CGRect)dan override func intrinsicContentSize() -> CGSizemenjadi override var intrinsicContentSize : CGSize Nikmati!
DoK
sayangnya saya tidak membuatnya bekerja. Saya mencoba dengan kode kami cepat 5 override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }
Nazmul Hasan
7

Cukup gunakan autolayout:

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

Selesai

Tim Shadel
sumber
Anda juga dapat melakukan hal yang sama dengan ketinggian.
ShadeToD
Luar biasa terima kasih.
Mike Taverne
7

Pilihan lain tanpa subclass adalah:

  1. Tetapkan label text
  2. sizeToFit()
  3. lalu tambah tinggi label sedikit untuk mensimulasikan bantalan

    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)
Orang
sumber
Anehnya, ini yang saya butuhkan, hanya dimodifikasi sedikit untuk ini: 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 dan -4 untuk pemusatan
Mofe Ejegi
6

Swift 3, solusi 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))
    }
}
andrewz
sumber
6

Dalam Swift 3

cara terbaik dan sederhana

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

}
SanRam
sumber
4

Subkelas UILabel. (File-File-Baru- CocoaTouchClass-make Subclass dari 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
 }
}

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

ATAU Kaitkan kelas UILabel khusus di Storyboard sebagai kelas Label.

AG
sumber
saya akan memilih jika Anda mengubah nilai-nilai hardcoded menjadi properti kelas, saya sudah menggunakan kode ini.
Juan Boero
@ Juan: drawTextInRect adalah properti kelas default dari UILabel yang tidak dapat kami timpa menggunakan kode. Praktik terbaik untuk subkelas UILabel dan menambahkan perubahan bingkai yang diperlukan. Bagaimanapun, itu nyaman sebagai fitur Warisan.
AG
ini benar, namun pada Swift 3 setidaknya, intrinsicContentSize bukan fungsi melainkan properti, jadi harus "menimpa var intrinsicContentSize: CGFloat {}" bukan "menimpa func intrinsicContentSize", hanya sebuah catatan.
joey
4

Sama seperti jawaban lain tetapi perbaiki bug. Ketika label.widthdikendalikan oleh tata letak otomatis, kadang-kadang teks akan dipotong.

@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))
    }
}
PowHu
sumber
2

Padding mudah (Swift 3.0, jawaban 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)))
        }

  }
Odemolliens
sumber
2

Elaborasi atas jawaban Mundi.

yaitu menempelkan label dalam UIViewdan menegakkan bantalan melalui Tata Letak Otomatis. Contoh:

terlihat seperti UILabel empuk

Gambaran:

1) Buat UIView("panel"), dan atur penampilannya.

2) Buat UILabeldan tambahkan ke panel.

3) Tambahkan kendala untuk menegakkan bantalan.

4) Tambahkan panel ke hierarki tampilan Anda, lalu posisikan panel.

Detail:

1) Buat tampilan panel.

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

2) Buat label, tambahkan ke panel sebagai subview.

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

3) Tambahkan batasan antara tepi label dan panel. Ini memaksa panel untuk menjaga jarak dari label. yaitu "bantalan"

Editorial: melakukan semua ini dengan tangan super-membosankan, verbal dan rawan kesalahan. Saya sarankan Anda memilih pembungkus Tata Letak Otomatis dari github atau menulis sendiri

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) Tambahkan panel ke hierarki tampilan Anda dan kemudian tambahkan batasan pemosisian. mis. peluk sisi kanan tableViewCell, seperti pada contoh gambar.

Catatan: Anda hanya perlu menambahkan batasan posisi, bukan batasan dimensi: Tata Letak Otomatis akan menyelesaikan tata letak berdasarkan intrinsicContentSizelabel dan batasan yang ditambahkan sebelumnya.

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

Gunakan kode ini jika Anda menghadapi masalah pemangkasan teks saat menerapkan padding.

@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)
    }
}
ceekay
sumber
Terima kasih telah memposting, saya mencari solusi untuk padding + trimming. Menurut saya solusi Anda mematahkan label.numberOfLines = 0 yang saya butuhkan. Ada solusi?
Don
1

Mirip dengan jawaban lain, tetapi dengan kelas fungsi untuk memasang bantalan secara dinamis:

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))
    }
}
Pelanes
sumber
1

Salah satu solusi pragmatis adalah menambahkan label kosong dengan tinggi dan warna yang sama dengan label utama. Atur spasi leading / trailing ke label utama menjadi nol, sejajarkan pusat vertikal, dan buat lebar margin yang Anda inginkan.

pks1981
sumber
1

Jika Anda ingin menambahkan padding 2px di sekitar textRect, lakukan saja ini:

let insets = UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2)
label.frame = UIEdgeInsetsInsetRect(textRect, insets)
zs2020
sumber
apa itu textRect? label.frame?
Gustavo Baiocchi Costa
1

Jika Anda tidak ingin atau perlu menggunakan UILabel @IBInspectable / @IBDesignable di Storyboard (saya pikir itu terlalu lambat), maka lebih bersih menggunakan UIEdgeInsets daripada 4 CGFloats yang berbeda.

Contoh kode untuk 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)
    }
}
Simon Backx
sumber
1

Objektif-C

Berdasarkan jawaban Tai Le di sana yang mengimplementasikan fitur di dalam IB Designable, inilah versi Objective-C.

Letakkan ini di YourLabel.h

@interface YourLabel : UILabel

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

@end

Dan ini akan masuk dalam 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

Anda kemudian dapat memodifikasi inset YourLabel secara langsung di Interface Builder setelah menentukan kelas di dalam XIB atau storyboard, nilai default inset menjadi nol.

lmarceau
sumber
0

Jalan mudah

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

}
Alex Freitas
sumber
0

Padding mudah:

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

    }
AG
sumber
Versi 3.0 di halaman bawah
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])
Maksim Gridin
sumber