Aggiunta di spazio / riempimento a un UILabel


187

Ho un punto in UILabelcui voglio aggiungere spazio in alto e in basso. Con un'altezza minima limitata, l'ho modificato in:

inserisci qui la descrizione dell'immagine

EDIT: Per fare questo ho usato:

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

    } 

Ma devo trovare un metodo diverso perché se scrivo più di due righe il problema è lo stesso:

inserisci qui la descrizione dell'immagine


1
Possibile duplicato del margine del testo
UILabel

Finalmente abbiamo finalmente capito esattamente come farlo correttamente, in tutti i casi dinamici, come un perfetto sostituto drop-in per UILabel senza bisogno di riprogettare o altri problemi. PHEW. stackoverflow.com/a/58876988/294884
Fattie

Risposte:


122

Se vuoi restare con UILabel, senza sottoclassarlo, Mundi ti ha dato una soluzione chiara.

Se in alternativa, si desidera evitare di avvolgere UILabel con un UIView, è possibile utilizzare UITextView per abilitare l'uso di UIEdgeInsets (padding) o la sottoclasse UILabel per supportare UIEdgeInsets.

L'uso di UITextView dovrebbe solo fornire gli inserti (OBJ-C):

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

In alternativa, se si esegue la sottoclasse di UILabel , un esempio di questo approccio potrebbe essere la sostituzione del metodo drawTextInRect
(OBJ-C)

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

È inoltre possibile fornire alla nuova sottoclasse UILabel le variabili degli inserti per TOP, LEFT, BOTTOM e RIGHT.

Un codice di esempio potrebbe essere:

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:

Da quello che ho visto, sembra che devi sovrascrivere la dimensione intrinseca di UILabel durante la sottoclasse.

Quindi dovresti sovrascrivere intrinsicContentSize come:

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

E aggiungi il seguente metodo per modificare i tuoi inserti, invece di modificarli singolarmente:

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

Aggiornerà le dimensioni di UILabel in modo che corrispondano alle inserzioni dei bordi e coprano le necessità multilinea a cui si fa riferimento.

Modifica n. 2

Dopo aver cercato un po 'ho trovato questo Gist con un IPInsetLabel. Se nessuna di queste soluzioni funziona, puoi provarla.

Modifica n. 3

C'era una domanda simile (duplicata) su questo argomento.
Per un elenco completo delle soluzioni disponibili, vedere questa risposta: margine di testo UILabel


Scusate ma ho già usato: `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 ))} `non funziona perché il risultato è lo stesso, non funziona in modo dinamico ..
Annachiara,

Hai provato con un UITextView invece di un UILabel? O hai davvero bisogno di usare un UILabel?
nunofmendes,

@Annachiara controlla la modifica che ho apportato. Vedi se funziona.
nunofmendes,

Ok. Ha funzionato la visualizzazione del testo? Ci scusiamo per non aver scritto in Swift ma sono ancora in modalità Obj-C. Il mio obiettivo con quel codice era di aiutarti a raggiungere qualche conclusione. Spero di averlo fatto.
nunofmendes,

1
Utilizzando TextView e alcune impostazioni dello storyboard e self.textview.textContainerInset = UIEdgeInsetsMake (0, 10, 10, 10); Finalmente funziona! Grazie !
Annachiara,

208

Ho provato con Swift 4.2 , speriamo che funzioni per te!

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

Oppure puoi usare CocoaPods qui https://github.com/levantAJ/PaddingLabel

pod 'PaddingLabel', '1.2'

inserisci qui la descrizione dell'immagine


6
La larghezza di uilabel non cambia, facendo diventare il testo "..."
neobie

1
@Tai Le, grazie per averlo condiviso, l'ho usato in vista tabella, non so perché il testo venga tagliato, ad es. lo studente diventa studen,
vishwa.deepak il

1
@Tim forse hai intenzione di usaremin
ielyamani

4
Una parola di avvertimento qui. Sto usando questa soluzione in una sottoclasse UILabel. Quando si utilizzano queste etichette in modalità multilinea, in un UIStackView verticale, c'è un problema. A volte l'etichetta sembra avvolgere il testo senza ridimensionare correttamente l'etichetta, quindi una parola o 2 finisce mancante dalla fine della stringa. Non ho una soluzione in questo momento. Lo scrivo qui se ne faccio uno. Ho passato ore a cercare questo problema, prima di provare che era qui.
Darren Black,

1
Per farlo funzionare in quelle situazioni, devi sovrascrivere "setBounds" e impostare self.preferredMaxLayoutWidth alla larghezza dei limiti, meno le tue
inserzioni

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

solo un piccolo commento: imposta questa classe per etichettare nella finestra di ispezione identità (classe personalizzata) e usa il nuovo attributo nella finestra di ispezione degli attributi chiamato padding etichetta. anche sotto l'imbottitura 5 non ha effetto
iman kazemayni,

3
Questo non funziona sempre correttamente con le etichette multilinea, perché quando l'etichetta calcola la sua altezza, assume un'imbottitura zero.
fhucho,

76

Puoi farlo correttamente da IB:

  1. cambia il testo in attribuito

testo attribuito

  1. vai all'elenco a discesa con "..."

inserisci qui la descrizione dell'immagine

  1. vedrai alcune proprietà di riempimento per le righe, i paragrafi e il testo che cambiano il rientro della prima riga o qualsiasi cosa tu voglia

inserisci qui la descrizione dell'immagine

  1. controlla il risultato

inserisci qui la descrizione dell'immagine


1
Nel mio storyboard posso vedere cambiare il testo ma quando eseguo l'app. Il testo non mostra la modifica ... T_T .. la mia etichetta è all'interno di una cella personalizzata, ci sono problemi?
A. Trejo,

1
@ A.Trejo Può essere la cella personalizzata impostare la proprietà dell'etichetta in fase di esecuzione.
Pierre-Yves Guillemet,

1
Le modifiche possono apparire sullo storyboard ma quando si esegue l'app non ci sono modifiche.
Rhenz,

4
Questo non è applicabile quando si imposta il testo a livello di codice.
Nij,

1
Questa non è la risposta Hai solo il controllo del rientro della prima riga, ma non il riempimento in tutte le direzioni.
rommex,

49

SWIFT 4

Soluzione facile da usare, disponibile per tutti i bambini UILabel nel progetto.

Esempio:

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

Estensione UILabel

import UIKit

extension UILabel {
    private struct AssociatedKeys {
        static var padding = UIEdgeInsets()
    }

    public var padding: UIEdgeInsets? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.padding) as? UIEdgeInsets
        }
        set {
            if let newValue = newValue {
                objc_setAssociatedObject(self, &AssociatedKeys.padding, newValue as UIEdgeInsets?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
            }
        }
    }

    override open func draw(_ rect: CGRect) {
        if let insets = padding {
            self.drawText(in: rect.inset(by: insets))
        } else {
            self.drawText(in: rect)
        }
    }

    override open var intrinsicContentSize: CGSize {
        guard let text = self.text else { return super.intrinsicContentSize }

        var contentSize = super.intrinsicContentSize
        var textWidth: CGFloat = frame.size.width
        var insetsHeight: CGFloat = 0.0
        var insetsWidth: CGFloat = 0.0

        if let insets = padding {
            insetsWidth += insets.left + insets.right
            insetsHeight += insets.top + insets.bottom
            textWidth -= insetsWidth
        }

        let newSize = text.boundingRect(with: CGSize(width: textWidth, height: CGFloat.greatestFiniteMagnitude),
                                        options: NSStringDrawingOptions.usesLineFragmentOrigin,
                                        attributes: [NSAttributedString.Key.font: self.font], context: nil)

        contentSize.height = ceil(newSize.size.height) + insetsHeight
        contentSize.width = ceil(newSize.size.width) + insetsWidth

        return contentSize
    }
}

1
ti preghiamo di spiegare brevemente la tua risposta e di non inserire solo il codice.
lmiguelvargasf,

4
La tua estensione annulla il valore 0 in numeroOfLines
Antoine Bodart

Questo è fantastico, ma ho problemi con più righe, anche se ho aggiunto numeri di righe 2 o ne ho lasciato 0, ne mostra sempre una. sai come risolverlo?
Mago Nicolas Palacios,

1
@AntoineBodart sei riuscito a risolvere il problema numberOfLines?
Mago Nicolas Palacios,

@ AntoineBodart, @Mago Nicolas Palacios - È stato risolto!
iEvgen Podkorytov,

47

Basta usare a UIViewcome superview e definire un margine fisso per l'etichetta con layout automatico.


1
drawTextInRect funziona solo per 1 riga, intrinsicContentSizenon funziona con imbottitura orizzontale. Avvolgere UILabel in UIView è il modo migliore per andare
onmyway133

8
Se sei in IB, ora è il momento di ricordare il menu Editor -> Incorpora in -> Visualizza. Seleziona prima UILabel :)
Graham Perks,

46

Basta usare un UIButton, che è già incorporato. Disattiva tutte le funzionalità dei pulsanti extra e hai un'etichetta su cui puoi impostare le installazioni dei bordi.

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
Ehi, questo è un ottimo consiglio! Nessuna estensione richiesta! MrGreen
Felipe Ferri il

2
L'impostazione isUserInteractionEnabled = falseè utile per disabilitarlo.
MCXC l'

Ottimo consiglio ... Preferirei fare questo piuttosto che cercare un'estensione.
Ross,

1
Bel suggerimento, con il grande vantaggio che può essere fatto anche in Interface Builder
Ely

1
La migliore soluzione senza sottoclasse ed ecc.
MeGaPk

16

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

Uso:

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)

Risultato:


Funziona, ma sai come farlo accettare più righe? Basta cambiare init con "PaddingLabel (withInsets: 8, 8, 16, 16)"
Camilo Ortegón

16

Imbottitura a UILabel, soluzione completa. Aggiornato per il 2020.

Si scopre che ci sono tre cose da fare.

1. Deve chiamare textRect # forBounds con la nuova dimensione più piccola

2. Deve sovrascrivere drawText con la nuova dimensione più piccola

3. Se una cella di dimensioni dinamiche, deve regolare intrinsicContentSize

Nell'esempio tipico di seguito, l'unità di testo è in una vista tabella, vista pila o costruzione simile, che le conferisce una larghezza fissa . Nell'esempio vogliamo un'imbottitura di 60,20,20,24.

Quindi, prendiamo intrinsicContentSize "esistente" e in realtà aggiungiamo 80 all'altezza .

Ripetere ...

Devi letteralmente "ottenere" l'altezza calcolata "finora" dal motore, e cambiare quel valore.

Trovo che questo processo sia confuso, ma è così che funziona. Per me, Apple dovrebbe esporre una chiamata chiamata "Calcolo preliminare dell'altezza".

In secondo luogo, dobbiamo effettivamente utilizzare la chiamata textRect # forBounds con le nostre nuove dimensioni più piccole .

Quindi in textRect # forBounds prima riduciamo le dimensioni e poi chiamiamo super.

Mettere in guardia! È necessario chiamare Super dopo , non prima!

Se indaghi attentamente su tutti i tentativi e le discussioni in questa pagina, questo è il problema esatto. Si noti che alcune soluzioni "sembrano quasi funzionare", ma qualcuno segnalerà che in determinate situazioni non funzionerà. Questo è davvero il motivo esatto: confusamente devi "chiamare super dopo", non prima.

Quindi, se lo chiami super "nell'ordine sbagliato", di solito funziona, ma non per determinate lunghezze di testo specifiche .

Ecco un esempio visivo esatto di "erroneamente fare il primo super":

inserisci qui la descrizione dell'immagine

Si noti che i margini 60,20,20,24 sono corretti MA il calcolo delle dimensioni è in realtà errato, poiché è stato eseguito con il modello "super first" in textRect # forBounds.

Fisso:

Si noti che solo ora il motore textRect # forBounds sa come eseguire correttamente il calcolo :

inserisci qui la descrizione dell'immagine

Finalmente!

Ancora una volta, in questo esempio UILabel viene utilizzato nella situazione tipica in cui la larghezza è fissa. Quindi in intrinsicContentSize dobbiamo "aggiungere" l'altezza extra complessiva che desideriamo. (Non è necessario "aggiungere" in alcun modo alla larghezza, sarebbe privo di significato in quanto è stato risolto.)

Quindi in textRect # forBounds si ottengono i limiti "suggeriti finora" mediante autolayout, si sottraggono i margini e solo successivamente si richiama nuovamente al motore textRect # forBounds, vale a dire in super, il che ti darà un risultato.

Finalmente e semplicemente in drawText, ovviamente, si disegna nella stessa casella più piccola.

Accidenti!

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
}

Di nuovo. Si noti che le risposte su questo e altri QA "quasi" corretti presentano il problema nella prima immagine sopra: il "super è nel posto sbagliato" . È necessario forzare la dimensione più grande in intrinsicContentSize e quindi in textRect # forBounds è necessario prima ridurre i limiti del primo suggerimento e quindi chiamare super.

Riepilogo: è necessario "chiamare super last " in textRect # forBounds

Questo è il segreto

Si noti che non è necessario e non è necessario chiamare inoltre invalidate, sizeThatFits, needsLayout o qualsiasi altra chiamata forzata. Una soluzione corretta dovrebbe funzionare correttamente nel normale ciclo di estrazione del layout automatico.


Mancia:

Se stai lavorando con caratteri monospace, ecco un ottimo consiglio: 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))
    } 
}

utilizzo:

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

ASPETTA: in alcuni casi ho riscontrato un problema con questo. In precedenza questa era la risposta più corretta. Ho inserito la risposta corretta di seguito.
Fattie,

Ho incluso un'immagine nella mia risposta che mostra il problema
Fattie,

9

Codice Swift 3 con esempio di implementazione

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

Non dimenticare di aggiungere il nome classe UIMarginLabel nell'oggetto etichetta storyboard. Buona programmazione!


8

Secondo Swift 4.2 (Xcode 10 beta 6) "UIEdgeInsetsInsetRect" è obsoleto. Ho anche dichiarato il pubblico di classe per renderlo più utile.

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

Funziona bene. Ma provo ad usarlo all'interno di CollectionViewCell e non si ridimensiona bene dopo il riutilizzo (evento dopo sizeToFit e layoutIfNeeded). Qualche id su come ridimensionarlo?
Bogy,

1
Ho aggiornato sizeToFit () per farlo funzionare con una vista riutilizzabile
Bogy,

sizeToFit()dovrebbe essere pubblico come: "Il metodo di istanza di sostituzione deve essere accessibile quanto il suo tipo allegato"
psyFi

7

Ho modificato un po 'la risposta accettata. C'è un problema quando leftInsete rightInsetaumentando, una parte del testo scomparirà, b / c la larghezza dell'etichetta verrà ridotta ma l'altezza non aumenta come in figura:

etichetta di imbottitura con dimensioni del contenuto intrinseco errate

Per risolvere questo problema è necessario ricalcolare l'altezza del testo come segue:

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

e risultato:

etichetta di imbottitura con giusta dimensione di contenuto intrinseco

Spero di aiutare alcune persone nella mia stessa situazione.


2
Se si prevede di utilizzare Swift 3.0 , è necessario modificare i nomi delle funzioni, poiché il nuovo linguaggio Apple rompe completamente la precedente definizione di funzioni. Quindi, override func drawTextInRect(rect: CGRect)diventa override func drawText(in rect: CGRect)e override func intrinsicContentSize() -> CGSizediventa override var intrinsicContentSize : CGSize Enjoy!
DoK,

sfortunatamente non l'ho fatto funzionare. Ho provato con il nostro codice rapido 5 override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }
Nazmul Hasan,

7

Usa il layout automatico:

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

Fatto.


Puoi anche fare lo stesso con l'altezza.
ShadeToD

Eccellente grazie.
Mike Taverne,

7

Un'altra opzione senza sottoclasse sarebbe:

  1. Imposta etichetta text
  2. sizeToFit()
  3. quindi aumentare leggermente l'altezza dell'etichetta per simulare l'imbottitura

    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)

Sorprendentemente, questo era tutto ciò di cui avevo bisogno, ho appena modificato un po 'questo: 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)i -10 e -4 per la centralizzazione
Mofe Ejegi,

6

Swift 3, soluzione iOS10:

open class UIInsetLabel: UILabel {

    open var insets : UIEdgeInsets = UIEdgeInsets() {
        didSet {
            super.invalidateIntrinsicContentSize()
        }
    }

    open override var intrinsicContentSize: CGSize {
        var size = super.intrinsicContentSize
        size.width += insets.left + insets.right
        size.height += insets.top + insets.bottom
        return size
    }

    override open func drawText(in rect: CGRect) {
        return super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }
}

6

In Swift 3

modo migliore e semplice

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

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

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

OPPURE Associare la classe UILabel personalizzata su Storyboard come classe Label.


voterei se cambi quei valori codificati in proprietà di classe, sto già usando questo codice.
Juan Boero,

@ Juan: drawTextInRect è una proprietà di classe predefinita di UILabel che non possiamo ignorare usando il codice. La migliore pratica per sottoclassare UILabel e aggiungere il cambio di frame richiesto. Comunque, è conveniente come funzionalità di ereditarietà.
AG,

questo è corretto, tuttavia almeno a partire da Swift 3, intrinsicContentSize non è una funzione ma piuttosto una proprietà, quindi dovrebbe essere "override var intrinsicContentSize: CGFloat {}" anziché "override func intrinsicContentSize", solo una nota.
Joey,

4

Proprio come altre risposte ma correggi un bug. Quando label.widthè controllato dal layout automatico, a volte il testo verrà ritagliato.

@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

Imbottitura semplice (Swift 3.0, risposta 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

Un'elaborazione della risposta di Mundi.

vale a dire incorporare un'etichetta in una UIViewe applicare l'imbottitura tramite Auto Layout. Esempio:

sembra un UILabel imbottito

Panoramica:

1) Creare un UIView("pannello") e impostarne l'aspetto.

2) Crea un UILabele aggiungilo al pannello.

3) Aggiungi vincoli per imporre il riempimento.

4) Aggiungi il pannello alla gerarchia della vista, quindi posiziona il pannello.

Dettagli:

1) Creare la vista del pannello.

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

2) Crea l'etichetta, aggiungila al pannello come sottoview.

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

3) Aggiungi vincoli tra i bordi dell'etichetta e il pannello. Questo costringe il pannello a mantenere una distanza dall'etichetta. cioè "imbottitura"

Editoriale: fare tutto questo a mano è noioso, dettagliato e soggetto a errori. Ti suggerisco di scegliere un wrapper di layout automatico da Github o di scriverne uno tu stesso

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) Aggiungi il pannello alla gerarchia della vista, quindi aggiungi i vincoli di posizionamento. ad esempio abbracciare il lato destro di un tableViewCell, come nell'immagine di esempio.

Nota: è necessario solo aggiungere vincoli posizionali, non vincoli dimensionali: Layout automatico risolverà il layout basandosi sia intrinsicContentSizesull'etichetta che sui vincoli aggiunti in precedenza.

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

Utilizzare questo codice se si riscontrano problemi di taglio del testo durante l'applicazione del riempimento.

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

Grazie per la pubblicazione, sto cercando una soluzione per quanto riguarda imbottitura + rifilatura. Mi sembra che la tua soluzione rompa label.numberOfLines = 0 di cui ho bisogno. Qualche soluzione?
Don

1

Simile ad altre risposte, ma con una classe func per impostare dinamicamente il padding:

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

Una soluzione pragmatica è quella di aggiungere etichette vuote della stessa altezza e colore dell'etichetta principale. Impostare lo spazio iniziale / finale sull'etichetta principale su zero, allineare i centri verticali e rendere la larghezza il margine desiderato.


1

Se vuoi aggiungere un'imbottitura 2px attorno al textRect, procedi nel seguente modo:

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

che cos'è textRect? label.frame?
Gustavo Baiocchi Costa,

1

Se non vuoi o non devi usare un UIBabel @IBInspectable / @IBDesignable in Storyboard (penso che comunque siano resi troppo lenti), allora è più pulito usare UIEdgeInsets invece di 4 diversi CGFloat.

Esempio di codice per 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

Objective-C

Basato sulla risposta Tai Le che implementa la funzionalità all'interno di IB Designable, ecco la versione Objective-C.

Metti questo in YourLabel.h

@interface YourLabel : UILabel

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

@end

E questo andrebbe in 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

È quindi possibile modificare gli inserti YourLabel direttamente in Interface Builder dopo aver specificato la classe all'interno di XIB o storyboard, con il valore predefinito degli inserti pari a zero.


0

Modo semplice

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

Imbottitura facile:

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

    }

Versione 3.0 in fondo alla pagina
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])
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.