UILabel'e boşluk / dolgu ekleme


187

UILabelÜstte ve altta boşluk eklemek istediğim bir yer var . Kısıtlamada minimun yüksekliği ile değiştirdim:

resim açıklamasını buraya girin

EDIT: Bunu yapmak için kullandım:

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

    } 

Ama farklı bir yöntem bulmalıyım çünkü ikiden fazla satır yazarsam sorun aynıdır:

resim açıklamasını buraya girin



Sonunda, tüm dinamik durumlarda, tam olarak nasıl düzgün bir şekilde yapılacağını, yeniden düzenlemeye veya başka sorunlara gerek kalmadan UILabel için mükemmel bir yedek olarak anladık. Uf. stackoverflow.com/a/58876988/294884
Fattie

Yanıtlar:


122

UILabel ile alt sınıflama yapmadan sadık kalmak istiyorsanız, Mundi size net bir çözüm sundu.

Alternatif olarak, UILabel'i bir UIView ile sarmaktan kaçınmaya istekli olursanız, UIEdgeInsets'i desteklemek için UIEdgeInsets (dolgu) veya alt sınıf UILabel kullanımını etkinleştirmek için UITextView kullanabilirsiniz.

Bir UITextView kullanmanın sadece ekleri (OBJ-C) sağlaması gerekir:

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

Alternatif olarak, UILabel alt sınıfını kullanıyorsanız , bu yaklaşıma bir örnek drawTextInRect yöntemini
(OBJ-C) geçersiz kılar

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

Ayrıca yeni alt sınıf UILabel'ınıza TOP, LEFT, BOTTOM ve RIGHT için iç değişkenler sağlayabilirsiniz.

Örnek bir kod şunlar olabilir:

.H (OBJ-C) cinsinden

float topInset, leftInset,bottomInset, rightInset;

Mm olarak (OBJ-C)

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

DÜZENLEME # 1:

Gördüğüm kadarıyla, alt sınıflandırma yaparken UILabel intrinsicContentSize geçersiz kılmak zorunda gibi görünüyor.

Bu yüzden intrinsicContentSize değerini aşağıdaki gibi geçersiz kılmalısınız :

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

Ve eklerinizi tek tek düzenlemek yerine düzenlemek için aşağıdaki yöntemi ekleyin:

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

UILabel'inizin boyutunu kenar iç metinlerine uyacak ve bahsettiğiniz çok satırlı gereksinimi karşılayacak şekilde güncelleyecektir.

Düzenle # 2

Biraz arama yaptıktan sonra bir IPInsetLabel ile bu Gist bulduk. Bu çözümlerin hiçbiri işe yaramazsa deneyebilirsiniz.

# 3'ü düzenle

Bu konuda da benzer bir soru vardı (mükerrer).
Kullanılabilir çözümlerin tam listesi için şu cevaba bakın: UILabel metin kenar boşluğu


Üzgünüm ama alreay kullandım: `func drawTextInRect (rect: CGRect) {var insets: UIEdgeInsets = UIEdgeInsets (üst: 0.0, sol: 10.0, alt: 0.0, sağ: 10.0) geçersiz kıl super.drawTextInRect (UIEdgeInsetsInsetRect (rect, insets ))} `` çalışmıyor çünkü sonuç aynı, dinamik olarak çalışma ...
Annachiara

UILabel yerine bir UITextView ile denediniz mi? Yoksa gerçekten bir UILabel kullanmanız mı gerekiyor?
nunofmendes

@Annachiara yaptığım düzenlemeyi kontrol et. İşe yarayıp yaramadığına bakın.
nunofmendes

Tamam. Metin görünümü işe yaradı mı? Swift'te yazamadığım için üzgünüm ama hala Obj-C modundayım. Bu kodla hedefim, bir sonuca varmanıza yardımcı olmaktı. Umarım yaptı.
nunofmendes

1
TextView ve bazı storyboard ayarlarını ve self.textview.textContainerInset = UIEdgeInsetsMake (0, 10, 10, 10); Sonunda işe yarıyor! Teşekkürler !
Annachiara

208

Swift 4.2 ile denedim , umarım sizin için çalışır!

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

Veya burada CocoaPod'ları kullanabilirsiniz https://github.com/levantAJ/PaddingLabel

pod 'PaddingLabel', '1.2'

resim açıklamasını buraya girin


6
Uilabel genişliği değişmiyor, bu da metnin "..." olmasına neden oluyor
neobie

1
@Tai Le, paylaştığınız için teşekkürler, tablo görünümünde kullandım, neden metni kırptığını bilmiyorum, örneğin. öğrenci studen olur,
vishwa.deepak

1
@Tim belki de kullanmak min
istediniz

4
Burada bir uyarı. Bu çözümü bir UILabel alt sınıfında kullanıyorum. Bu etiketleri çok satırlı modda kullanırken, dikey bir UIStackView'da bir sorun vardır. Bazen etiket, etiketi doğru şekilde boyutlandırmadan metni sarar gibi görünür - böylece bir kelime veya 2 dizenin sonundan kaybolur. Şu anda bir çözümüm yok. Bunu yaparsam buraya yazarım. Burada olduğunu kanıtlamadan önce bu konuyu araştırmak için saatler harcadım.
Darren Black

1
Bu durumlarda çalışması için "setBounds" ayarını geçersiz kılmanız ve self.preferredMaxLayoutWidth değerini sınırların genişliğine ayarlamanız gerekir, eksi sol ve sağ
ekleriniz

82

Hızlı 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
      }
   }
}

sadece küçük bir yorum: bu sınıfı kimlik denetçisinde (özel sınıf) etiketlenecek şekilde ayarlayın ve etiket dolgusu olarak adlandırılan özellik denetçisinde yeni özellik kullanın. Ayrıca 5 dolgu altında etkisiz
iman kazemayni

3
Bu çok satırlı etiketlerle her zaman doğru çalışmaz, çünkü etiket yüksekliğini hesapladığında sıfır dolgu olduğunu varsayar.
fhucho

76

IB'den düzgün bir şekilde yapabilirsiniz:

  1. metni ilişkilendirilmiş olarak değiştir

atfedilen metin

  1. "..." ile açılır listeye git

resim açıklamasını buraya girin

  1. satırların, paragrafların ve metin değişikliği girintisinin ilk satırına veya istediğiniz bir şeye ilişkin bazı dolgu özellikleri

resim açıklamasını buraya girin

  1. sonucu kontrol et

resim açıklamasını buraya girin


1
Film şeridimde metin değişikliğini görebiliyorum ancak uygulamayı çalıştırdığımda. Metin değişikliği göstermiyor ... T_T .. Etiketim özel bir hücrenin içinde, herhangi bir sorun var mı?
A. Trejo

1
@ A. Trejo Özel hücreniz label özelliğini çalışma zamanında ayarlayabilir.
Pierre-Yves Guillemet

1
Değişiklikler film şeridinde görünebilir, ancak uygulamayı çalıştırdığınızda herhangi bir değişiklik olmaz.
Rhenz

4
Metni programlı olarak ayarladığınızda bu geçerli değildir.
Nij

1
Cevap bu değil. Dolgu her yöne değil, yalnızca ilk satır girintisini kontrol edebilirsiniz.
rommex

49

SWIFT 4

Projedeki tüm UILabel çocukları için kolay kullanımlı çözüm.

Misal:

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

UILabel Uzantısı

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
lütfen cevabınızı kısaca açıklayınız ve sadece posta kodu yazmayınız.
lmiguelvargasf

4
Uzantınız 0 değerini numberOfLines ile iptal eder
Antoine Bodart

Bu harika, ama birden çok satırla ilgili sorunlar yaşıyorum, hatta satır 2'yi eklediğimi veya 0'da bıraktığımı düşündüm, her zaman birini gösteriyor. nasıl çözeceğini biliyor musun?
Mago Nicolas Palacios

1
@AntoineBodart numberOfLines problemini çözmeyi başardınız mı?
Mago Nicolas Palacios

@ AntoineBodart, @Mago Nicolas Palacios - Çözüldü!
iEvgen Podkorytov

47

Sadece bir kullanmak UIViewbir Superview olarak ve otomatik düzen etikete sabit bir marj tanımlar.


1
drawTextInRect sadece 1 satır için intrinsicContentSizeçalışır, yatay dolgu ile çalışmaz. Wrap UILabel UIView içinde gitmek için iyi bir yoldur
onmyway133

8
IB'deyseniz, Düzenleyici -> Yerleştir -> Görünüm menüsünü hatırlamanın zamanı geldi. İlk önce UILabel'inizi seçin :)
Graham Perks

46

Sadece yerleşik olan bir UIButton kullanın. Tüm ekstra düğme özelliklerini kapatın ve kenar iç kısımlarını ayarlayabileceğiniz bir etiketiniz var.

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
Hey bu harika bir ipucu! Uzantı gerekmez! :-D
Felipe Ferri

2
Ayarı isUserInteractionEnabled = falsedevre dışı bırakmak için kullanışlıdır.
mxcl

Harika bir ipucu ... Bunu uzatma yapmaktan ziyade yapmayı tercih ederim.
Ross

1
Arayüz Oluşturucu'da da yapılabilmesi büyük avantajla güzel ipucu
Ely

1
Alt sınıflama vb. Olmadan en iyi çözüm
MeGaPk

16

Storyboard olmadan:

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

Kullanımı:

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)

Sonuç:


Çalışıyor, ancak birden fazla satırı kabul etmesini nasıl biliyorsunuz? Sadece "PaddingLabel (withInsets: 8, 8, 16, 16)" ile
init'i değiştirin

16

Dolgu a UILabel, tam çözüm. 2020 için güncellendi.

O orada çıkıyor üç şey yapılmalıdır.

1. textRect # forBounds öğesini yeni küçük boyuta çağırmalısınız

2. Yeni küçük boyutta drawText'i geçersiz kılmalı

3. Dinamik olarak boyutlandırılmış bir hücre, intrinsicContentSize ayarlamalıdır

Aşağıdaki tipik örnekte, metin birimi tablo görünümünde, yığın görünümünde veya benzer bir yapıdadır, bu da ona sabit bir genişlik verir . Örnekte 60,20,20,24 dolgu istiyoruz.

Böylece, "varolan" intrinsicContentSize almak ve aslında yüksekliğe 80 ekleyin .

Tekrarlamak için ...

Kelimenin tam anlamıyla, motor tarafından "şimdiye kadar hesaplanan yüksekliği" almanız ve bu değeri değiştirmeniz gerekir .

Bu süreci kafa karıştırıcı buluyorum, ama işte böyle çalışıyor. Benim için Apple, "ön yükseklik hesaplaması" gibi bir şeyi açığa çıkarmalıdır.

İkinci olarak, textRect # forBounds çağrısını yeni küçük boyutumuzla kullanmamız gerekiyor .

TextRect # forBounds'da, önce boyutu küçültür ve sonra süper çağırırız.

Uyarmak! Sen gerekir süper çağrı sonrasında , daha önce değil!

Bu sayfadaki tüm girişimleri ve tartışmaları dikkatlice araştırırsanız, sorun tam olarak budur. Bazı çözümlerin "neredeyse işe yaradığına" dikkat edin, ancak birileri belirli durumlarda işe yaramayacağını bildirecektir. Bu gerçekten de kesin neden - kafa karıştırıcı bir şekilde, daha sonra değil, “sonradan süper aramalısınız”.

Bu nedenle, süper "yanlış sırada" olarak adlandırılırsa, genellikle çalışır, ancak belirli metin uzunlukları için geçerli değildir .

"Yanlış süper yapmak" için tam bir görsel örnek:

resim açıklamasını buraya girin

60,20,20,24 kenar boşluklarının doğru olduğuna dikkat edin.

Sabit:

TextRect # forBounds motorunun yalnızca şimdi hesaplamanın nasıl düzgün bir şekilde yapılacağını bildiğine dikkat edin :

resim açıklamasını buraya girin

En sonunda!

Yine, bu örnekte UILabel, genişliğin sabitlendiği tipik durumda kullanılmaktadır. Yani intrinsicContentSize içinde istediğimiz toplam ekstra yüksekliği "eklememiz" gerekir. (Genişliğe herhangi bir şekilde "eklemeniz" gerekmez, bu sabit olduğu için anlamsızdır.)

Daha sonra textRect # forBounds'ta, autolayout tarafından "şimdiye kadar önerilen" sınırlarını alırsınız, kenar boşluklarınızı çıkarırsınız ve ancak daha sonra textRect # forBounds motoruna tekrar çağırabilirsiniz, bu da size bir sonuç verir.

Son olarak ve drawText'te elbette aynı küçük kutuda çizin.

Uf!

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
}

Bir kere daha. Bu ve "neredeyse" doğru olan diğer KG'deki cevapların yukarıdaki ilk görüntüde sorun yaşadığını unutmayın - "süper yanlış yerdedir" . İntrinsicContentSize içinde boyutu daha büyük zorlamalısınız ve daha sonra textRect # forBounds'ta önce ilk öneri sınırlarını küçültmeli ve sonra süper çağırmalısınız.

Özet: textRect # forBounds içinde " süper son çağır" gerekir

Bu sır.

Ayrıca geçersiz kılmayı, sizeThatFits, needsLayout veya başka bir zorlama çağrısını çağırmanız gerekmediğini ve buna gerek olmadığını unutmayın. Doğru otomatik çözüm, normal otomatik yerleşim çekme döngüsünde düzgün çalışmalıdır.


İpucu:

Tek aralıklı yazı tipleriyle çalışıyorsanız, harika bir ipucu: 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))
    } 
}

kullanımı:

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

BEKLE - bazı durumlarda bununla ilgili bulduğum bir sorun var aslında. Daha önce bu en doğru cevaptı. Aşağıda doğru cevabı verdim.
Fattie

Cevabımı sorunu gösteren bir resim
ekledim

9

Uygulama Örneği ile Swift 3 Kodu

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

Film şeridi etiket nesnesine sınıf adı UIMarginLabel eklemeyi unutmayın. Mutlu Kodlama!


8

Uyarınca Swift 4.2 (Xcode 10 beta 6) "UIEdgeInsetsInsetRect" kullanımdan kaldırılmıştır. Ayrıca sınıfı daha kullanışlı hale getirmek için kamuoyuna ilan ettim.

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

İyi çalışıyor. Ama ben bir CollectionViewCell içinde kullanmaya çalışın ve yeniden kullanımdan sonra iyi boyutlandırmaz (sizeToFit ve layoutIfNeeded sonra olay). Herhangi bir kimlik nasıl yeniden boyutlandırılır?
Bogy

1
Ben güncelleme sizeToFit () o yeniden görünümü ile çalışması için yaptılar
Bogy

sizeToFit()şu şekilde herkese açık olmalıdır: "Geçersiz kılma örneği yöntemi, kapalı türü olarak erişilebilir olmalıdır"
psyFi

7

Kabul edilen cevapta biraz düzenleme yaptım. Sorun olduğunda leftInsetve rightInsetarttıkça, metnin bir kısmı kaybolacak, b / c etiket genişliği daralacak, ancak yükseklik şekil olarak artmayacak:

içerik içeriği yanlış olan dolgu etiketi

Bu sorunu çözmek için metnin yüksekliğini aşağıdaki gibi yeniden hesaplamanız gerekir:

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

ve sonuç:

doğru içerik boyutuna sahip dolgu etiketi

Benimle aynı durumda olan bazı insanlara yardım etmeyi umuyorum.


2
Swift 3.0 kullanmayı planlıyorsanız , yeni Apple dili önceki işlev tanımını tamamen bozduğundan işlev adlarını değiştirmeniz gerekir. Böylece override func drawTextInRect(rect: CGRect)olur override func drawText(in rect: CGRect)ve override func intrinsicContentSize() -> CGSizeolur override var intrinsicContentSize : CGSize tadını çıkarın!
DoK

ne yazık ki işe yaramadı. Kod hızlı override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }
5'imizi

7

Otomatik yerleşimi kullanmanız yeterlidir:

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

Bitti.


Aynısını yükseklik ile de yapabilirsiniz.
ShadeToD

Mükemmel, teşekkürler.
Mike Taverne

7

Alt sınıflama olmadan başka bir seçenek:

  1. Etiketi ayarla text
  2. sizeToFit()
  3. sonra dolguyu simüle etmek için etiket yüksekliğini biraz artırın

    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)

Şaşırtıcı bir şekilde, tüm ihtiyacım olan 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)
buydu

6

Swift 3, iOS10 çözümü:

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

Swift 3'te

en iyi ve basit yol

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

Alt sınıf UILabel. (Dosya-Yeni-Dosya- CocoaTouchClass-UILabel Alt Sınıfı olun).

//  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'da:

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

VEYA Storyboard'daki özel UILabel sınıfını Label'in sınıfı olarak ilişkilendirin.


sınıf özelliklerine bu sabit kodlanmış değerleri değiştirirseniz oy kullanmak, zaten bu kodu kullanıyorum.
Juan Boero

@ Juan: drawTextInRect, kod kullanarak geçersiz kılamadığımız varsayılan bir UILabel sınıfı özelliğidir. UILabel alt sınıfını oluşturmak ve gerekli çerçeve değişikliği eklemek için en iyi uygulama. Her neyse, Kalıtım özelliği olarak uygundur.
AG

ancak bu doğru, ancak en azından Swift 3'ten itibaren, intrinsicContentSize bir işlev değil, bir özelliktir, bu nedenle "geçersiz kılma func intrinsicContentSize" yerine, sadece bir not olmalıdır.
joey

4

Tıpkı diğer cevaplar gibi ama bir hatayı düzeltin. Ne zaman label.widthotomatik düzeni tarafından kontrol edilir, bazen metin kırpılır.

@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

Kolay dolgu (Swift 3.0, Alvin George cevap):

  class NewLabel: UILabel {

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

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

  }

2

Mundi'nin cevabı üzerine bir ayrıntı.

yani bir etikete a gömmek UIViewve Otomatik Yerleşim üzerinden dolgu uygulamak. Misal:

yastıklı bir UILabel gibi görünüyor

Genel bakış:

1) Bir UIView("panel" ) oluşturun ve görünümünü ayarlayın.

2) a oluşturun UILabelve panele ekleyin.

3) Dolguyu zorunlu kılmak için kısıtlamalar ekleyin.

4) Paneli görünüm hiyerarşinize ekleyin, ardından paneli konumlandırın.

Detaylar:

1) Panel görünümünü oluşturun.

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

2) Etiketi oluşturun, panele alt görünüm olarak ekleyin.

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

3) Etiketin kenarları ile panel arasına sınırlamalar ekleyin. Bu, paneli etiketten uzak durmaya zorlar. yani "dolgu"

Editoryal: Bütün bunları elle yapmak aşırı sıkıcı, ayrıntılı ve hataya açıktır. Github'dan bir Otomatik Düzen sarıcı seçmenizi veya kendiniz yazmanızı öneririm

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) Paneli görünüm hiyerarşinize ekleyin ve ardından konumlandırma kısıtlamaları ekleyin. örneğin, bir imageView örneğinde olduğu gibi bir tableViewCell'in sağ tarafına sarın.

Not: boyutsal kısıtlamalar değil, yalnızca konumsal kısıtlamalar eklemeniz gerekir: Otomatik Düzen, yerleşimi hem intrinsicContentSizeetiketin hem de daha önce eklenen kısıtlamalara göre çözecektir .

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

Dolgu uygularken metin kırpma sorunuyla karşı karşıyaysanız bu kodu kullanın.

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

Gönderdiğiniz için teşekkür ederim, dolgu + düzeltme konusunda bir çözüm arıyorum. Bana çözümün ihtiyacım olan label.numberOfLines = 0 tatili gibi görünüyor. Herhangi bir çözüm var mı?
Don

1

Diğer cevaplara benzer, ancak dolguyu dinamik olarak ayarlamak için bir func sınıfı ile:

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

Pragmatik bir çözüm, ana etiketle aynı yükseklik ve renkte boş etiketler eklemektir. Ana etikete giden ön / arka boşluğu sıfıra ayarlayın, dikey merkezleri hizalayın ve genişliği istediğiniz kenar boşluğuna getirin.


1

TextRect'in çevresine 2px dolgu eklemek istiyorsanız, şunu yapın:

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

textRect nedir? label.frame?
Gustavo Baiocchi Costa

1

Storyboard'da @IBInspectable / @IBDesignable UILabel'ı kullanmak istemiyorsanız veya kullanmanız gerekmiyorsa (bunların zaten çok yavaş işlendiğini düşünüyorum), 4 farklı CGFloats yerine UIEdgeInsets kullanmak daha temizdir.

Swift 4.2 için kod örneği:

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

Objective-C

IB Designable'ın içindeki özelliği uygulayan Tai Le cevabına dayanarak , Objective-C versiyonu.

Bunu YourLabel.h dosyasına koyun

@interface YourLabel : UILabel

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

@end

Bu da YourLabel.m dosyasında

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

Daha sonra XIB veya film şeridi içindeki sınıfı belirledikten sonra InsLabel iç metinlerini doğrudan Arayüz Oluşturucu'da değiştirebilirsiniz; iç metinlerin varsayılan değeri sıfırdır.


0

Kolay yol

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

Kolay dolgu:

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

    }

Alt sayfada 3.0 sürümü
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])
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.