Hinzufügen von Speicherplatz / Polsterung zu einem UILabel


186

Ich habe eine Stelle, UILabelan der ich oben und unten Platz hinzufügen möchte. Mit minimaler Höhe in Einschränkung habe ich es geändert zu:

Geben Sie hier die Bildbeschreibung ein

EDIT: Um dies zu tun, habe ich verwendet:

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

    } 

Aber ich muss eine andere Methode finden, denn wenn ich mehr als zwei Zeilen schreibe, ist das Problem dasselbe:

Geben Sie hier die Bildbeschreibung ein


1
Mögliches Duplikat des UILabel-Textrandes
gblazex

Wir haben endlich herausgefunden, wie dies in allen dynamischen Fällen richtig funktioniert, als perfekter Ersatz für UILabel, ohne dass ein neues Layout oder andere Probleme erforderlich sind. PUH. stackoverflow.com/a/58876988/294884
Fattie

Antworten:


121

Wenn Sie bei UILabel bleiben möchten, ohne es zu unterordnen, hat Mundi Ihnen eine klare Lösung gegeben.

Wenn Sie alternativ vermeiden möchten, das UILabel mit einem UIView zu umschließen, können Sie UITextView verwenden, um die Verwendung von UIEdgeInsets (Auffüllen) zu aktivieren, oder UILabel der Unterklasse, um UIEdgeInsets zu unterstützen.

Bei Verwendung einer UITextView müssten nur die Einfügungen (OBJ-C) bereitgestellt werden :

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

Alternativ, wenn Sie UILabel unterordnen , würde ein Beispiel für diesen Ansatz die drawTextInRect- Methode
(OBJ-C) überschreiben.

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

Sie können Ihrem neuen UILabel der Unterklasse zusätzlich Einfügungsvariablen für TOP, LEFT, BOTTOM und RIGHT bereitstellen.

Ein Beispielcode könnte sein:

In .h (OBJ-C)

float topInset, leftInset,bottomInset, rightInset;

In .m (OBJ-C)

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

EDIT # 1:

Nach allem, was ich gesehen habe, müssen Sie die intrinsicContentSize des UILabels überschreiben, wenn Sie es in Unterklassen unterteilen.

Sie sollten intrinsicContentSize also wie folgt überschreiben :

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

Fügen Sie die folgende Methode hinzu, um Ihre Insets zu bearbeiten, anstatt sie einzeln zu bearbeiten:

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

Die Größe Ihres UILabel wird aktualisiert, um den Kanteneinsätzen zu entsprechen und die von Ihnen erwähnte mehrzeilige Notwendigkeit abzudecken.

Bearbeiten Sie # 2

Nach einigem Suchen habe ich diesen Gist mit einem IPInsetLabel gefunden. Wenn keine dieser Lösungen funktioniert, können Sie sie ausprobieren.

Bearbeiten Sie # 3

Es gab eine ähnliche Frage (Duplikat) zu dieser Angelegenheit.
Eine vollständige Liste der verfügbaren Lösungen finden Sie in dieser Antwort: UILabel-Textrand


Entschuldigung, aber ich habe bereits Folgendes verwendet: `überschreiben func drawTextInRect (rect: CGRect) {var insets: UIEdgeInsets = UIEdgeInsets (oben: 0.0, left: 10.0, bottom: 0.0, right: 10.0) super.drawTextInRect (UIEdgeInsetsInsetRect (rect, insets) ))} `es funktioniert nicht, weil das Ergebnis das gleiche ist, nicht dynamisch arbeiten ..
Annachiara

Haben Sie es mit einem UITextView anstelle eines UILabels versucht? Oder brauchen Sie wirklich ein UILabel?
Nunofmendes

@Annachiara überprüfe die Bearbeitung, die ich vorgenommen habe. Überprüfen Sie, ob es funktioniert.
Nunofmendes

OK. Hat es in der Textansicht funktioniert? Es tut mir leid, dass ich nicht in Swift geschrieben habe, aber ich bin immer noch im Obj-C-Modus. Mein Ziel mit diesem Code war es, Ihnen zu einer Schlussfolgerung zu verhelfen. Hoffe es hat.
Nunofmendes

1
Verwenden von TextView und einigen Storyboard-Einstellungen sowie self.textview.textContainerInset = UIEdgeInsetsMake (0, 10, 10, 10); Es funktioniert endlich! Vielen Dank !
Annachiara

206

Ich habe es auf Swift 4.2 versucht , hoffentlich funktioniert es für Sie!

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

Oder Sie können CocoaPods hier verwenden https://github.com/levantAJ/PaddingLabel

pod 'PaddingLabel', '1.2'

Geben Sie hier die Bildbeschreibung ein


6
Die uilabel Breite ändert sich nicht, was dazu führt, dass der Text "..." wird
neobie

1
@ Tai Le, danke fürs Teilen, ich habe es in der Tabellenansicht verwendet, ich weiß nicht, warum es den Text schneidet, z. Student wird studen,
vishwa.deepak

1
@ Tim vielleicht wollten Sie verwendenmin
ielyamani

4
Ein Wort der Warnung hier. Ich habe diese Lösung in einer UILabel-Unterklasse verwendet. Bei Verwendung dieser Beschriftungen im mehrzeiligen Modus in einer vertikalen UIStackView tritt ein Problem auf. Manchmal scheint das Etikett den Text zu umbrechen, ohne die Größe des Etiketts korrekt zu ändern. Daher fehlen am Ende der Zeichenfolge ein oder zwei Wörter. Ich habe momentan keine Lösung. Ich werde es hier aufschreiben, wenn ich eins mache. Ich habe mich stundenlang mit diesem Thema befasst, bevor ich bewiesen habe, dass es hier ist.
Darren Black

1
Damit es in solchen Situationen funktioniert, müssen Sie "setBounds" überschreiben und self.preferredMaxLayoutWidth auf die Breite der Grenzen abzüglich Ihrer linken und rechten Einfügungen setzen
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
      }
   }
}

Nur ein kleiner Kommentar: Setzen Sie diese Klasse im Identitätsinspektor (benutzerdefinierte Klasse) auf Label und verwenden Sie im Attributinspektor ein neues Attribut mit dem Namen Label Padding. auch unter 5 Polsterung ist wirkungslos
iman kazemayni

3
Dies funktioniert bei mehrzeiligen Beschriftungen nicht immer richtig, da bei der Berechnung der Höhe der Beschriftung davon ausgegangen wird, dass keine Auffüllung erfolgt.
Fhucho

76

Sie können es richtig von IB machen:

  1. Ändern Sie den Text in zugeschrieben

zugeschriebener Text

  1. Gehe zur Dropdown-Liste mit "..."

Geben Sie hier die Bildbeschreibung ein

  1. Sie werden einige Auffüllungseigenschaften für die Zeilen, Absätze und Text sehen, die die erste Zeile oder alles andere ändern

Geben Sie hier die Bildbeschreibung ein

  1. Überprüfen Sie das Ergebnis

Geben Sie hier die Bildbeschreibung ein


1
In meinem Storyboard kann ich sehen, wie sich der Text ändert, aber wenn ich die App starte. Der Text zeigt die Änderung nicht an ... T_T .. Mein Etikett befindet sich in einer benutzerdefinierten Zelle. Gibt es ein Problem?
A. Trejo

1
@ A.Trejo Möglicherweise legt Ihre benutzerdefinierte Zelle die Label-Eigenschaft zur Laufzeit fest.
Pierre-Yves Guillemet

1
Änderungen können im Storyboard angezeigt werden, aber wenn Sie die App ausführen, gibt es keine Änderungen.
Rhenz

4
Dies gilt nicht, wenn Sie Text programmgesteuert festlegen.
Nij

1
Dies ist nicht die Antwort. Sie haben nur die Kontrolle über den Einzug der ersten Zeile, nicht über die Polsterung in alle Richtungen.
Rommex

49

SWIFT 4

Einfach zu bedienende Lösung, verfügbar für alle UILabel-Kinder im Projekt.

Beispiel:

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

UILabel-Erweiterung

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
Bitte erläutern Sie kurz Ihre Antwort und geben Sie nicht nur die Postleitzahl an.
lmiguelvargasf

4
Ihre Erweiterung stornieren den 0-Wert zu numberOfLines
Antoine Bodart

Das ist großartig, aber ich habe Probleme mit mehreren Zeilen, obwohl ich die Anzahl der Zeilen 2 addiere oder bei 0 lasse, zeigt es immer eine. Sie wissen, wie man es löst?
Mago Nicolas Palacios

1
@AntoineBodart haben Sie es geschafft, das numberOfLines-Problem zu lösen?
Mago Nicolas Palacios

@ AntoineBodart, @Mago Nicolas Palacios - Wurde gelöst!
iEvgen Podkorytov

47

Verwenden Sie einfach a UIViewals Übersicht und definieren Sie einen festen Rand für das Etikett mit automatischem Layout.


1
drawTextInRect funktioniert nur für 1 Zeile, intrinsicContentSizefunktioniert nicht mit horizontaler Polsterung. UILabel in UIView einwickeln ist der gute Weg
onmyway133

8
Wenn Sie in IB sind, ist es jetzt an der Zeit, sich an das Menü Editor -> Einbetten -> Ansicht zu erinnern. Wählen Sie einfach zuerst Ihr UILabel aus :)
Graham Perks

46

Verwenden Sie einfach einen UIButton, der bereits integriert ist. Deaktivieren Sie alle zusätzlichen Schaltflächenfunktionen und Sie haben eine Beschriftung, auf der Sie Kanteninstets festlegen können.

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 das ist ein toller Tipp! Keine Erweiterungen erforderlich! :-D
Felipe Ferri

2
Die Einstellung isUserInteractionEnabled = falseist praktisch, um sie zu deaktivieren.
mxcl

Toller Tipp ... Ich würde das lieber tun, als eine Verlängerung anzustreben.
Ross

1
Netter Tipp, mit dem großen Vorteil, dass es auch im Interface Builder
Ely

1
Die beste Lösung ohne Unterklassen usw.
MeGaPk

16

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

Verwendung:

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)

Ergebnis:


Funktioniert, aber wissen Sie, wie Sie mehrere Zeilen akzeptieren können? Ändern Sie einfach init mit "PaddingLabel (withInsets: 8, 8, 16, 16)"
Camilo Ortegón

15

Polsterung einer UILabelvollständigen Lösung. Aktualisiert für 2020.

Es stellt sich heraus, dass drei Dinge getan werden müssen.

1. Muss textRect # forBounds mit der neuen kleineren Größe aufrufen

2. Muss drawText mit der neuen kleineren Größe überschreiben

3. Wenn es sich um eine Zelle mit dynamischer Größe handelt, muss intrinsicContentSize angepasst werden

Im folgenden typischen Beispiel befindet sich die Texteinheit in einer Tabellenansicht, Stapelansicht oder einer ähnlichen Konstruktion, wodurch sie eine feste Breite erhält . Im Beispiel wollen wir eine Polsterung von 60,20,20,24.

Daher nehmen wir die "vorhandene" intrinsicContentSize und addieren tatsächlich 80 zur Höhe .

Wiederholen ...

Sie müssen buchstäblich die vom Motor "bisher" berechnete Höhe "erhalten" und diesen Wert ändern .

Ich finde diesen Prozess verwirrend, aber so funktioniert es. Für mich sollte Apple einen Anruf mit dem Namen "vorläufige Höhenberechnung" veröffentlichen.

Zweitens müssen wir den Aufruf textRect # forBounds mit unserer neuen kleineren Größe verwenden .

In textRect # forBounds verkleinern wir zuerst die Größe und dann rufen super auf.

Aufmerksam! Sie müssen super danach anrufen , nicht vorher !

Wenn Sie alle Versuche und Diskussionen auf dieser Seite sorgfältig untersuchen, ist dies genau das Problem. Beachten Sie, dass einige Lösungen "fast zu funktionieren scheinen", aber dann wird jemand berichten, dass es in bestimmten Situationen nicht funktioniert. Dies ist in der Tat der genaue Grund - verwirrenderweise müssen Sie "super danach anrufen", nicht vorher.

Wenn Sie es also "in der falschen Reihenfolge" super nennen, funktioniert es normalerweise, aber nicht für bestimmte Textlängen .

Hier ist ein genaues visuelles Beispiel für "falsches Super zuerst machen":

Geben Sie hier die Bildbeschreibung ein

Beachten Sie, dass die Ränder 60,20,20,24 korrekt sind, ABER die Größenberechnung ist tatsächlich falsch, da sie mit dem Muster "super first" in textRect # forBounds durchgeführt wurde.

Fest:

Beachten Sie, dass die textRect # forBounds-Engine erst jetzt weiß, wie die Berechnung ordnungsgemäß durchgeführt wird :

Geben Sie hier die Bildbeschreibung ein

Schließlich!

Auch in diesem Beispiel wird das UILabel in der typischen Situation verwendet, in der die Breite festgelegt ist. In intrinsicContentSize müssen wir also die gewünschte zusätzliche Gesamthöhe "hinzufügen". (Sie müssen die Breite in keiner Weise "hinzufügen", das wäre bedeutungslos, da es feststeht.)

Dann erhalten Sie in textRect # forBounds die "bisher vorgeschlagenen" Grenzen durch Autolayout, subtrahieren Ihre Ränder und rufen erst dann erneut die textRect # forBounds-Engine auf, dh in super, wodurch Sie ein Ergebnis erhalten.

Schließlich und einfach in drawText zeichnen Sie natürlich in derselben kleineren Box.

Puh!

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
}

Noch einmal. Beachten Sie, dass die Antworten auf diese und andere Fragen, die "fast" richtig sind, das Problem im ersten Bild oben haben - das "Super ist am falschen Ort" . Sie müssen die Größe in intrinsicContentSize erhöhen und dann in textRect # forBounds zuerst die Grenzen des ersten Vorschlags verkleinern und dann super aufrufen.

Zusammenfassung: Sie müssen in textRect # forBounds "super last " aufrufen

Das ist das Geheimnis.

Beachten Sie, dass Sie nicht zusätzlich invalidate, sizeThatFits, needLayout oder einen anderen erzwungenen Aufruf aufrufen müssen und sollten. Eine korrekte Lösung sollte im normalen Autolayout-Ziehzyklus ordnungsgemäß funktionieren.


Trinkgeld:

Wenn Sie mit Monospace-Schriftarten arbeiten, finden Sie hier einen guten Tipp: 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))
    } 
}

Verwendung:

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

WARTEN - es gibt tatsächlich ein Problem, das ich in einigen Fällen damit gefunden habe. Bisher war dies die richtigste Antwort. Ich habe unten die richtige Antwort eingegeben.
Fattie

Ich habe ein Bild in meine Antwort aufgenommen, das das Problem zeigt
Fattie

9

Swift 3 Code mit Implementierungsbeispiel

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

Vergessen Sie nicht, den Klassennamen UIMarginLabel in das Storyboard-Label-Objekt einzufügen. Viel Spaß beim Codieren!


8

Per Swift 4.2 (Xcode 10 Beta 6) "UIEdgeInsetsInsetRect" veraltet ist. Ich habe die Klasse auch öffentlich gemacht, um sie nützlicher zu machen.

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

Es funktioniert gut. Ich versuche jedoch, es in einer CollectionViewCell zu verwenden, und die Größe ändert sich nach der Wiederverwendung nicht gut (Ereignis nach sizeToFit und layoutIfNeeded). Irgendeine ID, wie man die Größe ändert?
Bogy

1
Ich habe sizeToFit () aktualisiert, damit es mit wiederverwendbarer Ansicht funktioniert
Bogy

sizeToFit()sollte öffentlich sein als: "Überschreibende Instanzmethode muss so zugänglich sein wie ihr einschließender Typ"
psyFi

7

Ich habe die akzeptierte Antwort ein wenig bearbeitet. Es gibt ein Problem, wenn ein Teil des Textes verschwindet leftInsetund rightInsetzunimmt, b / c die Breite des Etiketts wird verengt, aber die Höhe nimmt nicht wie in der Abbildung zu:

Polsteretikett mit falscher intrinsischer Inhaltsgröße

Um dieses Problem zu beheben, müssen Sie die Texthöhe wie folgt neu berechnen:

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

und Ergebnis:

Polsteretikett mit der richtigen Größe des Inhalts

Ich hoffe, einigen Menschen in der gleichen Situation wie ich helfen zu können.


2
Wenn Sie Swift 3.0 verwenden möchten , müssen Sie die Funktionsnamen ändern, da die neue Apple-Sprache die vorherige Funktionsdefinition vollständig verletzt. So override func drawTextInRect(rect: CGRect)wird override func drawText(in rect: CGRect)und override func intrinsicContentSize() -> CGSizewird override var intrinsicContentSize : CGSize Genießen!
DoK

Leider habe ich es nicht zum Laufen gebracht. Ich habe es mit unserem Code Swift 5override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }
Nazmul Hasan

7

Eine andere Option ohne Unterklasse wäre:

  1. Bezeichnung setzen text
  2. sizeToFit()
  3. Erhöhen Sie dann die Etikettenhöhe ein wenig, um die Polsterung zu simulieren

    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)

Überraschenderweise war dies alles, was ich brauchte, nur ein wenig geändert: 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)die -10 und -4 für die Zentralisierung
Mofe Ejegi

6

Swift 3, iOS10-Lösung:

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

Verwenden Sie einfach Autolayout:

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

Getan.


Sie können dasselbe auch mit der Höhe tun.
ShadeToD

6

In Swift 3

bester und einfacher Weg

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

Unterklasse UILabel. (File-New-File-CocoaTouchClass-make-Unterklasse von 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
 }
}

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

ODER Ordnen Sie eine benutzerdefinierte UILabel-Klasse im Storyboard als Label-Klasse zu.


Ich würde abstimmen, wenn Sie diese fest codierten Werte in Klasseneigenschaften ändern. Ich verwende diesen Code bereits.
Juan Boero

@ Juan: drawTextInRect ist eine Standardklasseneigenschaft von UILabel, die wir nicht mit Code überschreiben können. Die beste Vorgehensweise, um UILabel zu unterklassifizieren und den erforderlichen Frame-Wechsel hinzuzufügen. Auf jeden Fall ist es praktisch als Vererbungsfunktion.
AG

Dies ist jedoch korrekt. Ab Swift 3 ist intrinsicContentSize jedoch keine Funktion, sondern eine Eigenschaft. Daher sollte "override var intrinsicContentSize: CGFloat {}" anstelle von "override func intrinsicContentSize" nur eine Anmerkung sein.
Joey

4

Genau wie andere Antworten, aber behebe einen Fehler. Wenn dies label.widthdurch das automatische Layout gesteuert wird, wird manchmal Text zugeschnitten.

@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

Einfache Polsterung (Swift 3.0, Antwort von Alvin George):

  class NewLabel: UILabel {

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

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

  }

2

Eine Ausarbeitung von Mundis Antwort.

dh Einbetten eines Etiketts in ein UIViewund Erzwingen des Auffüllens durch das automatische Layout. Beispiel:

sieht aus wie ein gepolstertes UILabel

Überblick:

1) Erstellen Sie ein UIView("Panel") und legen Sie dessen Erscheinungsbild fest.

2) Erstellen Sie ein UILabelund fügen Sie es dem Panel hinzu.

3) Fügen Sie Einschränkungen hinzu, um das Auffüllen zu erzwingen.

4) Fügen Sie das Bedienfeld Ihrer Ansichtshierarchie hinzu und positionieren Sie das Bedienfeld.

Einzelheiten:

1) Erstellen Sie die Panel-Ansicht.

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

2) Erstellen Sie das Etikett und fügen Sie es als Unteransicht zum Bedienfeld hinzu.

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

3) Fügen Sie Einschränkungen zwischen den Kanten des Etiketts und dem Bedienfeld hinzu. Dies zwingt das Bedienfeld, Abstand zum Etikett zu halten. dh "Polsterung"

Editorial: All dies von Hand zu tun ist sehr mühsam, ausführlich und fehleranfällig. Ich schlage vor, Sie wählen einen Auto-Layout-Wrapper aus Github oder schreiben selbst einen

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) Fügen Sie das Bedienfeld zu Ihrer Ansichtshierarchie hinzu und fügen Sie dann Positionierungsbeschränkungen hinzu. Umarmen Sie beispielsweise die rechte Seite einer tableViewCell wie im Beispielbild.

Hinweis: Sie müssen nur Positionsbeschränkungen hinzufügen, keine Dimensionsbeschränkungen: Das automatische Layout löst das Layout basierend auf intrinsicContentSizeder Beschriftung und den zuvor hinzugefügten Einschränkungen.

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

Verwenden Sie diesen Code, wenn beim Anwenden des Auffüllens Probleme mit dem Zuschneiden von Text auftreten.

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

Vielen Dank für die Veröffentlichung, ich suche eine Lösung in Bezug auf Polsterung + Trimmen. Mir scheint, Ihre Lösung bricht label.numberOfLines = 0, was ich brauche. Irgendeine Problemumgehung?
Don

1

Ähnlich wie bei anderen Antworten, jedoch mit einer Funkklasse, um die Polsterung dinamisch einzurichten:

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

Eine pragmatische Lösung besteht darin, leere Etiketten mit der gleichen Höhe und Farbe wie das Hauptetikett hinzuzufügen. Setzen Sie den vorderen / hinteren Bereich des Hauptetiketts auf Null, richten Sie die vertikalen Zentren aus und stellen Sie die Breite auf den gewünschten Rand ein.


1

Wenn Sie dem textRect eine 2px-Auffüllung hinzufügen möchten, gehen Sie wie folgt vor:

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

Was ist textRect? label.frame?
Gustavo Baiocchi Costa

1

Wenn Sie kein @IBInspectable / @IBDesignable UILabel in Storyboard verwenden möchten oder müssen (ich denke, diese werden sowieso zu langsam gerendert), ist es sauberer, UIEdgeInsets anstelle von 4 verschiedenen CGFloats zu verwenden.

Codebeispiel für 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

Ziel c

Basierend auf der Antwort von Tai Le , die die Funktion in einem IB Designable implementiert, ist hier die Objective-C-Version.

Fügen Sie dies in YourLabel.h ein

@interface YourLabel : UILabel

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

@end

Und das würde in YourLabel.m gehen

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

Sie können Ihre YourLabel-Insets dann direkt im Interface Builder ändern, nachdem Sie die Klasse in XIB oder Storyboard angegeben haben. Der Standardwert der Insets ist Null.


0

Einfacher Weg

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

Einfache Polsterung:

import UIKit

    class NewLabel: UILabel {

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

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

        override func drawRect(rect: CGRect) {

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

    }

3.0 Version auf der unteren Seite
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])
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.