Agregar espacio / relleno a un UILabel

187

Tengo un lugar UILabeldonde quiero agregar espacio en la parte superior e inferior. Con una altura mínima en restricción, lo he modificado a:

ingrese la descripción de la imagen aquí

EDITAR: Para hacer esto he usado:

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

    } 

Pero tengo que encontrar un método diferente porque si escribo más de dos líneas el problema es el mismo:

ingrese la descripción de la imagen aquí

Annachiara
fuente
1
Posible duplicado del margen de texto
gblazex
Finalmente finalmente descubrimos exactamente cómo hacer esto correctamente, en todos los casos dinámicos, como un reemplazo perfecto para UILabel sin necesidad de rediseño ni ningún otro problema. UF. stackoverflow.com/a/58876988/294884
Fattie

Respuestas:

122

Si quieres seguir con UILabel, sin subclasificarlo, Mundi te ha dado una solución clara.

Si, alternativamente, estaría dispuesto a evitar envolver el UILabel con una UIView, podría usar UITextView para habilitar el uso de UIEdgeInsets (relleno) o la subclase UILabel para admitir UIEdgeInsets.

Usar un UITextView solo necesitaría proporcionar las inserciones (OBJ-C):

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

Alternativa, si subclasifica UILabel , un ejemplo de este enfoque sería anular el método drawTextInRect
(OBJ-C)

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

Además, puede proporcionar a su nuevo UILabel subclasificado con variables de inserción para ARRIBA, IZQUIERDA, ABAJO y DERECHA.

Un código de ejemplo podría ser:

En .h (OBJ-C)

float topInset, leftInset,bottomInset, rightInset;

En .m (OBJ-C)

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

EDITAR # 1:

Por lo que he visto, parece que tienes que anular el tamaño de contenido intrínseco del UILabel al subclasificarlo.

Por lo tanto, debe anular intrinsicContentSize como:

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

Y agregue el siguiente método para editar sus inserciones, en lugar de editarlas individualmente:

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

Actualizará el tamaño de su UILabel para que coincida con las inserciones de los bordes y cubra la necesidad multilínea a la que se refirió.

Editar # 2

Después de buscar un poco, he encontrado este Gist con un IPInsetLabel. Si ninguna de esas soluciones funciona, puede probarla.

Editar # 3

Hubo una pregunta similar (duplicada) sobre este asunto.
Para obtener una lista completa de las soluciones disponibles, consulte esta respuesta: margen de texto de UILabel

nunofmendes
fuente
Lo siento, pero ya he usado: `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 ))} `no funciona porque el resultado es el mismo, no funciona dinámicamente ..
Annachiara
¿Intentaste con un UITextView en lugar de un UILabel? ¿O realmente necesitas usar un UILabel?
nunofmendes
@Annachiara revisa la edición que hice. A ver si funciona.
nunofmendes
Okay. ¿Funcionó la vista de texto? Perdón por no escribir en Swift pero todavía estoy en modo Obj-C. Mi objetivo con ese código era ayudarlo a llegar a alguna conclusión. Espero que lo haya hecho.
nunofmendes
1
Usando TextView y algunas configuraciones de guión gráfico y self.textview.textContainerInset = UIEdgeInsetsMake (0, 10, 10, 10); ¡Finalmente funciona! Gracias !
Annachiara
208

Lo he intentado con Swift 4.2 , ¡espero que funcione para ti!

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

O puede usar CocoaPods aquí https://github.com/levantAJ/PaddingLabel

pod 'PaddingLabel', '1.2'

ingrese la descripción de la imagen aquí

Tai Le
fuente
66
El ancho de uilabel no cambia, lo que hace que el texto se convierta en "..."
neobie
1
@Tai Le, gracias por compartir, lo he usado en la vista de tabla, no sé por qué está recortando el texto, por ejemplo. estudiante se convierte en estudiante,
vishwa.deepak
1
@Tim quizás hayas querido usarmin
ielyamani
44
Una palabra de advertencia aquí. He estado usando esta solución en una subclase de UILabel. Cuando se usan estas etiquetas en modo multilínea, en un UIStackView vertical, hay un problema. A veces, la etiqueta parece envolver el texto sin cambiar el tamaño de la etiqueta correctamente, por lo que una palabra o 2 terminan faltando al final de la cadena. No tengo una solución en este momento. Lo escribiré aquí si hago uno. Pasé horas hurgando en este problema, antes de demostrar que estaba aquí.
Darren Black el
1
Para que funcione en esas situaciones, debe anular "setBounds" y establecer self.preferredMaxLayoutWidth al ancho de los límites, menos sus inserciones izquierda y derecha
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
      }
   }
}
zombi
fuente
solo un pequeño comentario: configure esta clase para etiquetar en el inspector de identidad (clase personalizada) y use un nuevo atributo en el inspector de atributos denominado relleno de etiqueta. también debajo de 5 acolchado no tiene efecto
iman kazemayni
3
Esto no siempre funciona correctamente con etiquetas multilínea, porque cuando la etiqueta calcula su altura, supone un relleno cero.
fhucho
76

Puede hacerlo correctamente desde IB:

  1. cambiar el texto a atribuido

texto atribuido

  1. ir a la lista desplegable con "..."

ingrese la descripción de la imagen aquí

  1. verá algunas propiedades de relleno para las líneas, los párrafos y la sangría de cambio de texto en la primera línea o cualquier cosa que desee

ingrese la descripción de la imagen aquí

  1. verifica el resultado

ingrese la descripción de la imagen aquí

Pierre-Yves Guillemet
fuente
1
En mi guión gráfico puedo ver el cambio de texto, pero cuando ejecuto la aplicación. El texto no muestra el cambio ... T_T .. mi etiqueta está dentro de una celda personalizada, ¿hay algún problema?
A. Trejo
1
@ A.Trejo Puede ser su celda personalizada establezca la propiedad de etiqueta en tiempo de ejecución.
Pierre-Yves Guillemet
1
Los cambios pueden aparecer en el guión gráfico pero cuando ejecuta la aplicación no hay cambios.
Rhenz
44
Esto no es aplicable cuando configura el texto mediante programación.
Nij
1
Esta no es la respuesta. Solo tiene control sobre la sangría de la primera línea, no el relleno en todas las direcciones.
rommex
49

SWIFT 4

Solución fácil de usar, disponible para todos los niños UILabel en el proyecto.

Ejemplo:

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

Extensión UILabel

import UIKit

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

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

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

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

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

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

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

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

        return contentSize
    }
}
iEvgen Podkorytov
fuente
1
explique brevemente su respuesta y no solo publique el código.
lmiguelvargasf
44
Su extensión cancela el valor 0 de numberOfLines
Antoine Bodart el
Esto es genial, pero tengo problemas con varias líneas, incluso si agrego números de líneas 2 o salgo a 0, siempre muestra uno. sabes como resolverlo?
Mago Nicolas Palacios
1
@AntoineBodart ¿lograste resolver el problema numberOfLines?
Mago Nicolas Palacios
@ AntoineBodart, @Mago Nicolas Palacios - ¡Fue resuelto!
iEvgen Podkorytov
47

Simplemente use a UIViewcomo supervista y defina un margen fijo para la etiqueta con diseño automático.

Mundi
fuente
1
drawTextInRect funciona solo para 1 línea, intrinsicContentSizeno funciona con relleno horizontal. Envolver UILabel dentro de UIView es el buen camino a seguir
onmyway133
8
Si está en IB, ahora es el momento de recordar el menú Editor -> Incrustar en -> Ver. Solo selecciona tu UILabel primero :)
Graham Perks
46

Simplemente use un UIButton, ya está incorporado. Desactive todas las funciones de botón adicionales y tendrá una etiqueta en la que puede colocar los bordes de los bordes.

let button = UIButton()
button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
button.setTitle("title", for: .normal)
button.tintColor = .white // this will be the textColor
button.isUserInteractionEnabled = false
Steve M
fuente
2
¡Hola, este es un gran consejo! ¡No se requieren extensiones! :-D
Felipe Ferri
2
La configuración isUserInteractionEnabled = falsees útil para deshabilitarla.
mxcl
Gran consejo ... Prefiero hacer esto que ir por una extensión.
Ross
1
Un buen consejo, con la gran ventaja de que también se puede hacer en Interface Builder
Ely
1
La mejor solución sin subclases, etc.
MeGaPk
16

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

Resultado:

Alice Chan
fuente
Funciona, pero ¿sabes cómo hacer que acepte varias líneas? Simplemente cambie init con "PaddingLabel (withInsets: 8, 8, 16, 16)"
Camilo Ortegón
16

Relleno de un UILabel solución completa. Actualizado para 2020.

Resulta que hay tres cosas que deben hacerse.

1. Debe llamar a textRect # forBounds con el nuevo tamaño más pequeño

2. Debe anular drawText con el nuevo tamaño más pequeño

3. Si una celda de tamaño dinámico, debe ajustar intrinsicContentSize

En el ejemplo típico a continuación, la unidad de texto está en una vista de tabla, vista de pila o construcción similar, lo que le da un ancho fijo . En el ejemplo queremos un relleno de 60,20,20,24.

Por lo tanto, tomamos el tamaño de contenido intrínseco "existente" y en realidad agregamos 80 a la altura .

Repetir ...

Debe literalmente "obtener" la altura calculada "hasta ahora" por el motor, y cambiar ese valor.

Este proceso me parece confuso, pero así es como funciona. Para mí, Apple debería exponer una llamada llamada algo así como "cálculo preliminar de altura".

En segundo lugar, tenemos que usar la llamada textRect # forBounds con nuestro nuevo tamaño más pequeño .

Entonces, en textRect # forBounds primero hacemos el tamaño más pequeño y luego llamamos super.

¡Alerta! ¡ Debes llamar a Super después , no antes!

Si investiga cuidadosamente todos los intentos y discusiones en esta página, ese es el problema exacto. Observe que algunas soluciones "parecen funcionar casi", pero alguien informará que en ciertas situaciones no funcionará. Esta es de hecho la razón exacta: confusamente debes "llamar a super después", no antes.

Por lo tanto, si llama a super "en el orden incorrecto", generalmente funciona, pero no para ciertas longitudes de texto específicas .

Aquí hay un ejemplo visual exacto de "hacer super incorrectamente primero":

ingrese la descripción de la imagen aquí

Observe que los márgenes 60,20,20,24 son correctos PERO el cálculo del tamaño es realmente incorrecto, ya que se realizó con el patrón "súper primero" en textRect # forBounds.

Fijo:

Observe que solo ahora el motor textRect # forBounds sabe cómo hacer el cálculo correctamente :

ingrese la descripción de la imagen aquí

¡Finalmente!

Nuevamente, en este ejemplo, UILabel se está utilizando en la situación típica en la que el ancho es fijo. Entonces, en intrinsicContentSize tenemos que "agregar" la altura extra general que queremos. (No necesita "agregar" de ninguna manera al ancho, eso no tendría sentido ya que está arreglado).

Luego, en textRect # forBounds obtienes los límites "sugeridos hasta ahora" por autolayout, restas tus márgenes, y solo entonces vuelves a llamar al motor textRect # forBounds, es decir en super, que te dará un resultado.

Finalmente y simplemente en drawText, por supuesto, dibuja en el mismo cuadro más pequeño.

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

Una vez más. Tenga en cuenta que las respuestas sobre este y otros controles de calidad que son "casi" correctas sufren el problema en la primera imagen de arriba: el "súper está en el lugar equivocado" . Debe forzar el tamaño más grande en intrinsicContentSize y luego en textRect # forBounds primero debe reducir los límites de la primera sugerencia y luego llamar a super.

Resumen: debe "llamar a super last " en textRect # forBounds

Ese es el secreto

Tenga en cuenta que no es necesario ni adicionalmente llamar a invalidar, sizeThatFits, needsLayout o cualquier otra llamada forzada. Una solución correcta debería funcionar correctamente en el ciclo normal de extracción automática.


Propina:

Si está trabajando con fuentes monoespaciales, este es un gran consejo: https://stackoverflow.com/a/59813420/294884

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

uso:

let label = EdgeInsetLabel()
label.textInsets = UIEdgeInsets(top: 2, left: 6, bottom: 2, right: 6)
LE24
fuente
ESPERE: en algunos casos encontré un problema con esto. Anteriormente, esta era la respuesta más correcta. He puesto la respuesta correcta a continuación.
Fattie
He incluido una imagen en mi respuesta que muestra el problema
Fattie
9

Código Swift 3 con ejemplo de implementación

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

No olvide agregar el nombre de clase UIMarginLabel en el objeto de etiqueta del guión gráfico. ¡Feliz codificación!

mriaz0011
fuente
8

Según Swift 4.2 (Xcode 10 beta 6) "UIEdgeInsetsInsetRect" está en desuso. También he declarado la clase pública para que sea más útil.

public class UIPaddedLabel: UILabel {

    @IBInspectable var topInset: CGFloat = 5.0
    @IBInspectable var bottomInset: CGFloat = 5.0
    @IBInspectable var leftInset: CGFloat = 7.0
    @IBInspectable var rightInset: CGFloat = 7.0

    public override func drawText(in rect: CGRect) {
        let insets = UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        super.drawText(in: rect.inset(by: insets))
    }

    public override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(width: size.width + leftInset + rightInset,
                      height: size.height + topInset + bottomInset)
    }

    public override func sizeToFit() {
        super.sizeThatFits(intrinsicContentSize)
    }
}
Stéphane de Luca
fuente
Funciona bien. Pero trato de usarlo dentro de un CollectionViewCell y no cambia de tamaño bien después de la reutilización (evento después de sizeToFit y layoutIfNeeded). ¿Alguna identificación cómo cambiar su tamaño?
Bogy
1
Actualicé sizeToFit () para que funcione con vista reutilizable
Bogy
sizeToFit()debe ser público como: "El método de instancia de anulación debe ser tan accesible como su tipo de
cierre
7

Edité un poco en la respuesta aceptada. Hay un problema cuando aumenta leftInsety rightInset, una parte del texto desaparecerá, b / c el ancho de la etiqueta se reducirá pero la altura no aumenta como figura:

etiqueta de relleno con tamaño de contenido intrínseco incorrecto

Para resolver este problema, debe volver a calcular la altura del texto de la siguiente manera:

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

y resultado:

etiqueta de relleno con el tamaño de contenido intrínseco correcto

Espero ayudar a algunas personas en la misma situación que yo.

Quang Tran
fuente
2
Si planea usar Swift 3.0 , debe cambiar los nombres de las funciones, ya que el nuevo lenguaje de Apple rompe por completo la definición de función anterior. Entonces, se override func drawTextInRect(rect: CGRect)convierte override func drawText(in rect: CGRect)y se override func intrinsicContentSize() -> CGSizeconvierte en override var intrinsicContentSize : CGSize Enjoy!
DoK
desafortunadamente no lo hice funcionar. Lo intenté con nuestro código swift 5 override var intrinsicContentSize: CGSize { // .. return intrinsicSuperViewContentSize }
Nazmul Hasan
7

Solo usa autolayout:

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

Hecho.

Tim Shadel
fuente
También puedes hacer lo mismo con la altura.
ShadeToD
Excelente, gracias.
Mike Taverne
7

Otra opción sin subclasificar sería:

  1. Coloca una etiqueta text
  2. sizeToFit()
  3. luego aumente un poco la altura de la etiqueta para simular el relleno

    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)
Chico
fuente
Sorprendentemente, esto fue todo lo que necesitaba, solo modifiqué un poco esto: label.frame = CGRect( x: label.frame.origin.x - 10, y: label.frame.origin.y - 4, width: label.frame.width + 20,height: label.frame.height + 8)-10 y -4 para centralizar
Mofe Ejegi
6

Swift 3, solución iOS10:

open class UIInsetLabel: UILabel {

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

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

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

En Swift 3

mejor y simple manera

class UILabelPadded: UILabel {
     override func drawText(in rect: CGRect) {
     let insets = UIEdgeInsets.init(top: 0, left: 5, bottom: 0, right: 5)
     super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

}
Sanram
fuente
4

Subclase UILabel. (Archivo-Nuevo-Archivo- CocoaTouchClass-make Subclase de 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
 }
}

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

O Asociar la clase personalizada UILabel en Storyboard como la clase de Label.

AG
fuente
votaría si cambias esos valores codificados en propiedades de clase, ya estoy usando este código.
Juan Boero
@ Juan: drawTextInRect es una propiedad de clase predeterminada de UILabel que no podemos anular usando código. La mejor práctica para subclasificar UILabel y agregar el cambio de marco requerido. De todos modos, es conveniente como característica de herencia.
AG
esto es correcto, sin embargo, al menos a partir de Swift 3, intrinsicContentSize no es una función, sino una propiedad, por lo que debe "anular var intrinsicContentSize: CGFloat {}" en lugar de "anular func intrinsicContentSize", solo una nota.
joey
4

Al igual que otras respuestas, pero corrige un error. Cuando label.widthestá controlado por el diseño automático, a veces el texto se recorta.

@IBDesignable
class InsetLabel: UILabel {

    @IBInspectable var topInset: CGFloat = 4.0
    @IBInspectable var leftInset: CGFloat = 4.0
    @IBInspectable var bottomInset: CGFloat = 4.0
    @IBInspectable var rightInset: CGFloat = 4.0

    var insets: UIEdgeInsets {
        get {
            return UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        }
        set {
            topInset = newValue.top
            leftInset = newValue.left
            bottomInset = newValue.bottom
            rightInset = newValue.right
        }
    }

    override func sizeThatFits(_ size: CGSize) -> CGSize {
        var adjSize = super.sizeThatFits(size)
        adjSize.width += leftInset + rightInset
        adjSize.height += topInset + bottomInset
        return adjSize
    }

    override var intrinsicContentSize: CGSize {
        let systemContentSize = super.intrinsicContentSize
        let adjustSize = CGSize(width: systemContentSize.width + leftInset + rightInset, height: systemContentSize.height + topInset +  bottomInset) 
        if adjustSize.width > preferredMaxLayoutWidth && preferredMaxLayoutWidth != 0 {
            let constraintSize = CGSize(width: bounds.width - (leftInset + rightInset), height: .greatestFiniteMagnitude)
            let newSize = super.sizeThatFits(constraintSize)
            return CGSize(width: systemContentSize.width, height: ceil(newSize.height) + topInset + bottomInset)
        } else {
            return adjustSize
        }
    }

    override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: insets))
    }
}
PowHu
fuente
2

Relleno fácil (Swift 3.0, respuesta de Alvin George):

  class NewLabel: UILabel {

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

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

  }
Odemolliens
fuente
2

Una explicación sobre la respuesta de Mundi.

es decir, incrustar una etiqueta en UIVieway aplicar el relleno a través del diseño automático. Ejemplo:

parece un UILabel acolchado

Visión general:

1) Cree un UIView("panel") y configure su apariencia.

2) Cree un UILabely agréguelo al panel.

3) Agregue restricciones para forzar el relleno.

4) Agregue el panel a su jerarquía de vistas, luego coloque el panel.

Detalles:

1) Crear la vista del panel.

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

2) Cree la etiqueta, agréguela al panel como una subvista.

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

3) Agregue restricciones entre los bordes de la etiqueta y el panel. Esto obliga al panel a mantener una distancia de la etiqueta. es decir, "relleno"

Editorial: hacer todo esto a mano es súper tedioso, detallado y propenso a errores. Le sugiero que elija un contenedor de diseño automático de github o escriba uno usted mismo

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) Agregue el panel a su jerarquía de vistas y luego agregue restricciones de posicionamiento. por ejemplo, abrace el lado derecho de una tableViewCell, como en la imagen de ejemplo.

Nota: solo necesita agregar restricciones de posición, no restricciones dimensionales: el diseño automático resolverá el diseño en función tanto intrinsicContentSizede la etiqueta como de las restricciones agregadas anteriormente.

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

Use este código si enfrenta un problema de recorte de texto al aplicar el relleno.

@IBDesignable class PaddingLabel: UILabel {

    @IBInspectable var topInset: CGFloat = 5.0
    @IBInspectable var bottomInset: CGFloat = 5.0
    @IBInspectable var leftInset: CGFloat = 5.0
    @IBInspectable var rightInset: CGFloat = 5.0

    override func drawText(in rect: CGRect) {
        let insets = UIEdgeInsets.init(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

    override var intrinsicContentSize: CGSize {
        var intrinsicSuperViewContentSize = super.intrinsicContentSize
        let textWidth = frame.size.width - (self.leftInset + self.rightInset)
        let newSize = self.text!.boundingRect(with: CGSize(textWidth, CGFloat.greatestFiniteMagnitude), options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSFontAttributeName: self.font], context: nil)
        intrinsicSuperViewContentSize.height = ceil(newSize.size.height) + self.topInset + self.bottomInset
        return intrinsicSuperViewContentSize
    }
}

extension CGSize{
    init(_ width:CGFloat,_ height:CGFloat) {
        self.init(width:width,height:height)
    }
}
ceekay
fuente
Gracias por publicar, estoy buscando una solución en cuanto a relleno + recorte. Me parece que su solución rompe label.numberOfLines = 0 que necesito. ¿Alguna solución?
Don
1

Similar a otras respuestas, pero con una clase func para configurar dinámicamente el relleno:

class UILabelExtendedView: UILabel
{
    var topInset: CGFloat = 4.0
    var bottomInset: CGFloat = 4.0
    var leftInset: CGFloat = 8.0
    var rightInset: CGFloat = 8.0

    override func drawText(in rect: CGRect)
    {
        let insets: UIEdgeInsets = UIEdgeInsets(top: topInset, left: leftInset, bottom: bottomInset, right: rightInset)
        super.drawText(in: UIEdgeInsetsInsetRect(rect, insets))
    }

    override public var intrinsicContentSize: CGSize
    {
        var contentSize = super.intrinsicContentSize
        contentSize.height += topInset + bottomInset
        contentSize.width += leftInset + rightInset
        return contentSize
    }

    func setPadding(top: CGFloat, left: CGFloat, bottom: CGFloat, right: CGFloat){
        self.topInset = top
        self.bottomInset = bottom
        self.leftInset = left
        self.rightInset = right
        let insets: UIEdgeInsets = UIEdgeInsets(top: top, left: left, bottom: bottom, right: right)
        super.drawText(in: UIEdgeInsetsInsetRect(self.frame, insets))
    }
}
Pelanes
fuente
1

Una solución pragmática es agregar etiquetas en blanco de la misma altura y color que la etiqueta principal. Establezca el espacio inicial / final en la etiqueta principal a cero, alinee los centros verticales y haga que el ancho sea el margen deseado.

pks1981
fuente
1

Si desea agregar un relleno de 2px alrededor del textRect, simplemente haga esto:

let insets = UIEdgeInsets(top: -2, left: -2, bottom: -2, right: -2)
label.frame = UIEdgeInsetsInsetRect(textRect, insets)
zs2020
fuente
¿Qué es textRect? label.frame?
Gustavo Baiocchi Costa
1

Si no desea o necesita usar un UIBabel @IBInspectable / @IBDesignable en Storyboard (creo que de todos modos se procesan demasiado lento), entonces es más limpio usar UIEdgeInsets en lugar de 4 CGFloats diferentes.

Ejemplo de código para Swift 4.2:

class UIPaddedLabel: UILabel {
    var padding = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)

    public override func drawText(in rect: CGRect) {
        super.drawText(in: rect.inset(by: padding))
    }

    public override var intrinsicContentSize: CGSize {
        let size = super.intrinsicContentSize
        return CGSize(width: size.width + padding.left + padding.right,
                      height: size.height + padding.top + padding.bottom)
    }
}
Simon Backx
fuente
1

C objetivo

Basado en la respuesta de Tai Le que implementa la función dentro de un Designable IB, aquí está la versión Objective-C.

Pon esto en YourLabel.h

@interface YourLabel : UILabel

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

@end

Y esto iría en 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

Luego puede modificar las inserciones de YourLabel directamente en Interface Builder después de especificar la clase dentro del XIB o el guión gráfico, el valor predeterminado de las inserciones es cero.

lmarceau
fuente
0

Manera fácil

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        self.view.addSubview(makeLabel("my title",x: 0, y: 100, w: 320, h: 30))
    }

    func makeLabel(title:String, x:CGFloat, y:CGFloat, w:CGFloat, h:CGFloat)->UILabel{
        var myLabel : UILabel = UILabel(frame: CGRectMake(x,y,w,h))
        myLabel.textAlignment = NSTextAlignment.Right

        // inser last char to right
        var titlePlus1char = "\(title)1"
        myLabel.text = titlePlus1char
        var titleSize:Int = count(titlePlus1char)-1

        myLabel.textColor = UIColor(red:1.0, green:1.0,blue:1.0,alpha:1.0)
        myLabel.backgroundColor = UIColor(red: 214/255, green: 167/255, blue: 0/255,alpha:1.0)


        // create myMutable String
        var myMutableString = NSMutableAttributedString()

        // create myMutable font
        myMutableString = NSMutableAttributedString(string: titlePlus1char, attributes: [NSFontAttributeName:UIFont(name: "HelveticaNeue", size: 20)!])

        // set margin size
        myMutableString.addAttribute(NSFontAttributeName, value: UIFont(name: "HelveticaNeue", size: 10)!, range: NSRange(location: titleSize,length: 1))

        // set last char to alpha 0
        myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor(red:1.0, green:1.0,blue:1.0,alpha:0), range: NSRange(location: titleSize,length: 1))

        myLabel.attributedText = myMutableString

        return myLabel
    }


    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}
Alex Freitas
fuente
0

Acolchado fácil:

import UIKit

    class NewLabel: UILabel {

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

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

        override func drawRect(rect: CGRect) {

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

    }
AG
fuente
Versión 3.0 en la página inferior
odemolliens
0

Swift 4+

let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 10

// Swift 4.2++
label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])

// Swift 4.1--
label.attributedText = NSAttributedString(string: "Your text", attributes: [NSAttributedStringKey.paragraphStyle: paragraphStyle])
Maksim Gridin
fuente