UIButton: ¿cómo centrar una imagen y un texto usando imageEdgeInsets y titleEdgeInsets?

159

Si pongo solo una imagen en un botón y configuro imageEdgeInsets más cerca de la parte superior, la imagen permanece centrada y todo funciona como se esperaba:

[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)];

Si pongo solo un texto en un botón y configuro titleEdgeInsets más cerca de la parte inferior, el texto permanece centrado y todo funciona como se esperaba:

[button setTitle:title forState:UIControlStateNormal];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)];

Pero, si pongo las 4 líneas juntas, el texto interfiere con la imagen y ambas pierden la alineación central.

Todas mis imágenes tienen 30 píxeles de ancho, y si pongo 30 en el parámetro izquierdo de UIEdgeInsetMake para setTitleEdgeInsets, el texto se vuelve a centrar. El problema es que la imagen nunca se centra porque parece que depende del tamaño de button.titleLabel. Ya probé muchos cálculos con el tamaño del botón, el tamaño de la imagen, el tamaño de la etiqueta del título y nunca los centro perfectamente.

Alguien ya tuvo el mismo problema?

reinaldoluckman
fuente

Respuestas:

412

Para lo que vale, aquí hay una solución general para colocar la imagen centrada sobre el texto sin usar ningún número mágico. Tenga en cuenta que el siguiente código está desactualizado y probablemente debería usar una de las versiones actualizadas a continuación :

// the space between the image and text
CGFloat spacing = 6.0;

// lower the text and push it left so it appears centered 
//  below the image
CGSize imageSize = button.imageView.frame.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
  0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);

// raise the image and push it right so it appears centered
//  above the text
CGSize titleSize = button.titleLabel.frame.size;
button.imageEdgeInsets = UIEdgeInsetsMake(
  - (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);

La siguiente versión contiene cambios para admitir iOS 7+ que se han recomendado en los comentarios a continuación. No he probado este código yo mismo, así que no estoy seguro de qué tan bien funciona o si se rompería si se usara en versiones anteriores de iOS.

// the space between the image and text
CGFloat spacing = 6.0;

// lower the text and push it left so it appears centered 
//  below the image
CGSize imageSize = button.imageView.image.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
  0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);

// raise the image and push it right so it appears centered
//  above the text
CGSize titleSize = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName: button.titleLabel.font}];
button.imageEdgeInsets = UIEdgeInsetsMake(
  - (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);

// increase the content height to avoid clipping
CGFloat edgeOffset = fabsf(titleSize.height - imageSize.height) / 2.0;
button.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0);

Versión Swift 5.0

extension UIButton {
  func alignVertical(spacing: CGFloat = 6.0) {
    guard let imageSize = imageView?.image?.size,
      let text = titleLabel?.text,
      let font = titleLabel?.font
    else { return }

    titleEdgeInsets = UIEdgeInsets(
      top: 0.0,
      left: -imageSize.width,
      bottom: -(imageSize.height + spacing),
      right: 0.0
    )

    let titleSize = text.size(withAttributes: [.font: font])
    imageEdgeInsets = UIEdgeInsets(
      top: -(titleSize.height + spacing),
      left: 0.0,
      bottom: 0.0, right: -titleSize.width
    )

    let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0
    contentEdgeInsets = UIEdgeInsets(
      top: edgeOffset,
      left: 0.0,
      bottom: edgeOffset,
      right: 0.0
    )
  }
}
Jesse Crossen
fuente
55
Maravilloso, gracias! Creo que muchas otras respuestas se refieren a esto "de la manera difícil", esto se ve mucho mejor.
Joe D'Andrea
3
Encontré lo anterior para wok, pero no tengo absolutamente ningún modelo mental de cómo. ¿Alguien recibió un enlace a una explicación gráfica de las cosas que afectan los parámetros individuales de EdgeInsets? ¿Y por qué habría cambiado el ancho del texto?
Robert Atkins
3
Esto no funciona cuando la imagen se está reduciendo para ajustarse al botón. Parece que UIButton (al menos en iOS 7) usa image.size, no imageView.frame.size para sus cálculos de centrado.
Dan Jackson
55
@Hemang y Dan Jackson, estoy incorporando sus sugerencias sin probarlas yo mismo. Me parece un poco ridículo que originalmente escribí esto para iOS 4, y después de estas muchas versiones todavía tenemos que hacer ingeniería inversa del algoritmo de diseño de Apple para obtener una característica tan obvia. O al menos supongo que aún no hay una solución mejor a partir del flujo constante de votos a favor y las respuestas igualmente hackistas a continuación (sin intención de insultar).
Jesse Crossen
55
En iOS8, me pareció mejor usarlo en button.currentTitlelugar de button.titleLabel.textespecialmente si el texto del botón cambiara alguna vez. currentTitlese llena de inmediato, mientras que titleLabel.textpuede cambiar lentamente, lo que puede conducir a insertos desalineados.
mjangda
59

Encontrado cómo.

Primero, configure el texto de titleLabel(debido a los estilos, es decir, negrita, cursiva, etc.). Luego, use setTitleEdgeInsetsconsiderando el ancho de su imagen:

[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateNormal];
[button.titleLabel setFont:[UIFont boldSystemFontOfSize:10.0]];

// Left inset is the negative of image width.
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, -image.size.width, -25.0, 0.0)]; 

Después de eso, use setTitleEdgeInsetsconsiderando el ancho de los límites del texto:

[button setImage:image forState:UIControlStateNormal];

// Right inset is the negative of text bounds width.
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, -button.titleLabel.bounds.size.width)];

Ahora la imagen y el texto estarán centrados (en este ejemplo, la imagen aparece sobre el texto).

Salud.

reinaldoluckman
fuente
3
7 años, hombre. Realmente no lo recuerdo. Cuando pregunté, me respondí (mi respuesta fue la única en ese momento). Cambié la respuesta seleccionada cuando el autor de la respuesta seleccionada actual se centró en eliminar esos números mágicos. Entonces, en la respuesta seleccionada, puedes descubrir lo que significan.
reinaldoluckman
20

Puede hacerlo con esta extensión Swift, que se basó en parte en la respuesta de Jesse Crossen:

extension UIButton {
  func centerLabelVerticallyWithPadding(spacing:CGFloat) {
    // update positioning of image and title
    let imageSize = self.imageView.frame.size
    self.titleEdgeInsets = UIEdgeInsets(top:0,
                                        left:-imageSize.width,
                                        bottom:-(imageSize.height + spacing),
                                        right:0)
    let titleSize = self.titleLabel.frame.size
    self.imageEdgeInsets = UIEdgeInsets(top:-(titleSize.height + spacing),
                                        left:0,
                                        bottom: 0,
                                        right:-titleSize.width)

    // reset contentInset, so intrinsicContentSize() is still accurate
    let trueContentSize = CGRectUnion(self.titleLabel.frame, self.imageView.frame).size
    let oldContentSize = self.intrinsicContentSize()
    let heightDelta = trueContentSize.height - oldContentSize.height
    let widthDelta = trueContentSize.width - oldContentSize.width
    self.contentEdgeInsets = UIEdgeInsets(top:heightDelta/2.0,
                                          left:widthDelta/2.0,
                                          bottom:heightDelta/2.0,
                                          right:widthDelta/2.0)
  }
}

Esto define una función centerLabelVerticallyWithPadding que establece el título y las inserciones de imagen de manera adecuada.

También establece contentEdgeInsets, que creo que es necesario para garantizar que intrinsicContentSize todavía funcione correctamente, que necesitaría usar el diseño automático.

Creo que todas las soluciones que subclase UIButton son técnicamente ilegítimas, ya que no se supone que subclase los controles UIKit. Es decir, en teoría podrían romperse en futuras versiones.

alga
fuente
Pruebas en iOS9. La imagen aparece centrada, pero el texto aparece a la izquierda :(
endavid
13

Editar: actualizado para Swift 3

En caso de que esté buscando una solución rápida de la respuesta de Jesse Crossen, puede agregar esto a una subclase de UIButton:

override func layoutSubviews() {

    let spacing: CGFloat = 6.0

    // lower the text and push it left so it appears centered
    //  below the image
    var titleEdgeInsets = UIEdgeInsets.zero
    if let image = self.imageView?.image {
        titleEdgeInsets.left = -image.size.width
        titleEdgeInsets.bottom = -(image.size.height + spacing)
    }
    self.titleEdgeInsets = titleEdgeInsets

    // raise the image and push it right so it appears centered
    //  above the text
    var imageEdgeInsets = UIEdgeInsets.zero
    if let text = self.titleLabel?.text, let font = self.titleLabel?.font {
        let attributes = [NSFontAttributeName: font]
        let titleSize = text.size(attributes: attributes)
        imageEdgeInsets.top = -(titleSize.height + spacing)
        imageEdgeInsets.right = -titleSize.width
    }
    self.imageEdgeInsets = imageEdgeInsets

    super.layoutSubviews()
}
Thomas Verbeek
fuente
9

Aquí hay algunos ejemplos excelentes, pero no pude hacer que esto funcionara en todos los casos cuando también trataba con varias líneas de texto (ajuste de texto). Para que finalmente funcione, combiné un par de técnicas:

  1. Usé el ejemplo anterior de Jesse Crossen. Sin embargo, solucioné un problema de altura del texto y agregué la capacidad de especificar un margen de texto horizontal. El margen es útil cuando permite que el texto se ajuste para que no toque el borde del botón:

    // the space between the image and text
    CGFloat spacing = 10.0;
    float   textMargin = 6;
    
    // get the size of the elements here for readability
    CGSize  imageSize   = picImage.size;
    CGSize  titleSize   = button.titleLabel.frame.size;
    CGFloat totalHeight = (imageSize.height + titleSize.height + spacing);      // get the height they will take up as a unit
    
    // lower the text and push it left to center it
    button.titleEdgeInsets = UIEdgeInsetsMake( 0.0, -imageSize.width +textMargin, - (totalHeight - titleSize.height), +textMargin );   // top, left, bottom, right
    
    // the text width might have changed (in case it was shortened before due to 
    // lack of space and isn't anymore now), so we get the frame size again
    titleSize = button.titleLabel.bounds.size;
    
    button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0.0, 0.0, -titleSize.width );     // top, left, bottom, right        
  2. Asegúrese de configurar la etiqueta de texto para ajustar

    button.titleLabel.numberOfLines = 2; 
    button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
    button.titleLabel.textAlignment = UITextAlignmentCenter;
  3. Esto funcionará principalmente ahora. Sin embargo, tenía algunos botones que no mostraban su imagen correctamente. La imagen se desplazó hacia la derecha o hacia la izquierda (no estaba centrada). Así que utilicé una técnica de anulación de diseño UIButton para forzar el centrado de imageView.

    @interface CategoryButton : UIButton
    @end
    
    @implementation CategoryButton
    
    - (void)layoutSubviews
    {
        // Allow default layout, then center imageView
        [super layoutSubviews];
    
        UIImageView *imageView = [self imageView];
        CGRect imageFrame = imageView.frame;
        imageFrame.origin.x = (int)((self.frame.size.width - imageFrame.size.width)/ 2);
        imageView.frame = imageFrame;
    }
    @end
Tod Cunningham
fuente
Parece que esta es una buena solución, sin embargo, ¿no debería button.titleLabel.numberOfLines ser 0 de modo que pueda tener tantas líneas como desee?
Ben Lachman
En mi caso, solo quería hasta dos líneas. De lo contrario, la imagen tendría problemas con el tamaño total del botón.
Tod Cunningham
9

Hice un método para la respuesta de @ TodCunningham

 -(void) AlignTextAndImageOfButton:(UIButton *)button
 {
   CGFloat spacing = 2; // the amount of spacing to appear between image and title
   button.imageView.backgroundColor=[UIColor clearColor];
   button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
   button.titleLabel.textAlignment = UITextAlignmentCenter;
   // get the size of the elements here for readability
   CGSize imageSize = button.imageView.frame.size;
   CGSize titleSize = button.titleLabel.frame.size;

  // lower the text and push it left to center it
  button.titleEdgeInsets = UIEdgeInsetsMake(0.0, - imageSize.width, - (imageSize.height   + spacing), 0.0);

  // the text width might have changed (in case it was shortened before due to 
  // lack of space and isn't anymore now), so we get the frame size again
   titleSize = button.titleLabel.frame.size;

  // raise the image and push it right to center it
  button.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, -     titleSize.width);
 }
Bekir Onat Akin
fuente
7

Actualizado para Xcode 11+

Las inserciones descritas en mi respuesta original se han trasladado al inspector de tamaño en las versiones más recientes de Xcode. No estoy 100% claro sobre cuándo ocurrió el cambio, pero los lectores deberían revisar el inspector de tamaño si la información del recuadro no se encuentra en el inspector de atributos. A continuación se muestra una muestra de la nueva pantalla de inserción (ubicada en la parte superior del inspector de atributos de tamaño a partir de 11.5).

insets_moved_to_size_inspector

Respuesta original

No hay nada malo con las otras respuestas, sin embargo, solo quería señalar que el mismo comportamiento se puede lograr visualmente dentro de Xcode usando cero líneas de código. Esta solución es útil si no necesita un valor calculado o si está construyendo con un guión gráfico / xib (de lo contrario, se aplican otras soluciones).

Nota: entiendo que la pregunta del OP es una que requiere código. Solo estoy proporcionando esta respuesta para completar y como una alternativa lógica para aquellos que usan guiones gráficos / xibs.

Para modificar el espaciado en las vistas de imagen, título y contenido de un botón utilizando inserciones de borde, puede seleccionar el botón / control y abrir el inspector de atributos. Desplácese hacia el centro del inspector y busque la sección para inserciones de borde.

inserciones de borde

También se puede acceder y modificar las inserciones de borde específicas para la vista de título, imagen o contenido.

opciones de menú

Tommie C.
fuente
Lo que no entiendo es por qué parece que no puedo ingresar números negativos en el guión gráfico para algunos valores.
Daniel T.
¿Funciona esto para la nueva versión rápida? No puedo encontrar el atributo Edge en ningún lado
brockhampton
@brockhampton - ver respuesta actualizada para nueva ubicación
Tommie C.
6

No luches contra el sistema. Si sus diseños se vuelven demasiado complejos para administrarlos usando Interface Builder + quizás algún código de configuración simple, haga los diseños manualmente de una manera más simple usandolayoutSubviews - ¡para eso está! Todo lo demás equivaldrá a hacks.

Cree una subclase UIButton y anule su layoutSubviewsmétodo para alinear su texto e imagen mediante programación. O use algo como https://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.h para que pueda implementar layoutSubviews usando un bloque.

Steven Kramer
fuente
6

Subclase UIButton

- (void)layoutSubviews {
    [super layoutSubviews];
    CGFloat spacing = 6.0;
    CGSize imageSize = self.imageView.image.size;
    CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(self.frame.size.width, self.frame.size.height - (imageSize.height + spacing))];
    self.imageView.frame = CGRectMake((self.frame.size.width - imageSize.width)/2, (self.frame.size.height - (imageSize.height+spacing+titleSize.height))/2, imageSize.width, imageSize.height);
    self.titleLabel.frame = CGRectMake((self.frame.size.width - titleSize.width)/2, CGRectGetMaxY(self.imageView.frame)+spacing, titleSize.width, titleSize.height);
}
Alex
fuente
6

Respuesta actualizada de Jesse Crossen para Swift 4 :

extension UIButton {
    func alignVertical(spacing: CGFloat = 6.0) {
        guard let imageSize = self.imageView?.image?.size,
            let text = self.titleLabel?.text,
            let font = self.titleLabel?.font
            else { return }
        self.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: -(imageSize.height + spacing), right: 0.0)
        let labelString = NSString(string: text)
        let titleSize = labelString.size(withAttributes: [kCTFontAttributeName as NSAttributedStringKey: font])
        self.imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + spacing), left: 0.0, bottom: 0.0, right: -titleSize.width)
        let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
        self.contentEdgeInsets = UIEdgeInsets(top: edgeOffset, left: 0.0, bottom: edgeOffset, right: 0.0)
    }
}

Use de esta manera:

override func viewDidLayoutSubviews() {
    button.alignVertical()
}
Doci
fuente
Después de muchas, muchas horas. Esto fue lo único que funcionó :)
Harry Blue
4

Con este fragmento de código, obtendrás algo como esto alineación de título e imagen

extension UIButton {
    func alignTextUnderImage() {
        guard let imageView = imageView else {
                return
        }
        self.contentVerticalAlignment = .Top
        self.contentHorizontalAlignment = .Center
        let imageLeftOffset = (CGRectGetWidth(self.bounds) - CGRectGetWidth(imageView.bounds)) / 2//put image in center
        let titleTopOffset = CGRectGetHeight(imageView.bounds) + 5
        self.imageEdgeInsets = UIEdgeInsetsMake(0, imageLeftOffset, 0, 0)
        self.titleEdgeInsets = UIEdgeInsetsMake(titleTopOffset, -CGRectGetWidth(imageView.bounds), 0, 0)
    }
}
Bohdan Savych
fuente
2
Escribí una pequeña extensión para posicionar la imagen y el texto dentro del botón. Si está interesado, aquí está el código fuente. github.com/sssbohdan/ButtonAlignmentExtension/blob/master/…
Bohdan Savych
4

Extensión UIButton con sintaxis Swift 3+ :

extension UIButton {
    func alignImageAndTitleVertically(padding: CGFloat = 6.0) {
        let imageSize: CGSize = imageView!.image!.size
        titleEdgeInsets = UIEdgeInsetsMake(0.0, -imageSize.width, -(imageSize.height + padding), 0.0)
        let labelString = NSString(string: titleLabel!.text!)
        let titleSize = labelString.size(attributes: [NSFontAttributeName: titleLabel!.font])
        self.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + padding), 0.0, 0.0, -titleSize.width)
        let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
        self.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0)
    }
}

Respuesta original: https://stackoverflow.com/a/7199529/3659227

Disidente
fuente
3

Solo un pequeño cambio en la respuesta de Jesse Crossen que hizo que funcionara perfectamente para mí:

en vez de:

CGSize titleSize = button.titleLabel.frame.size;

He usado esto:

CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}];
Cesar
fuente
Bienvenido a SO! En lugar de agregar una respuesta por separado (para un cambio menor), puede escribir esto a Jesse directamente para que pueda verificar y actualizar su respuesta [aceptada] correctamente (si es necesario).
Hemang
3

El uso button.titleLabel.frame.size.widthfunciona bien siempre que la etiqueta sea lo suficientemente corta como para no truncarse. Sin embargo, cuando el texto de la etiqueta se trunca, el posicionamiento no funciona. Tomando

CGSize titleSize = [[[button titleLabel] text] sizeWithFont:[[button titleLabel] font]];

funciona para mí incluso cuando el texto de la etiqueta está truncado.

diciembre
fuente
Tienes un error tipográfico.
Bhimbim
2

Miré las respuestas existentes pero también descubrí que configurar el marco del botón es un primer paso importante.

Aquí hay una función que uso que se encarga de esto:

const CGFloat kImageTopOffset   = -15;
const CGFloat kTextBottomOffset = -25;

+ (void) centerButtonImageTopAndTextBottom: (UIButton*)         button 
                                     frame: (CGRect)            buttonFrame
                                      text: (NSString*)         textString
                                 textColor: (UIColor*)          textColor
                                      font: (UIFont*)           textFont
                                     image: (UIImage*)          image
                                  forState: (UIControlState)    buttonState
{
    button.frame = buttonFrame;

    [button setTitleColor: (UIColor*)       textColor
                 forState: (UIControlState) buttonState];

    [button setTitle: (NSString*) textString
            forState: (UIControlState) buttonState ];


    [button.titleLabel setFont: (UIFont*) textFont ];

    [button setTitleEdgeInsets: UIEdgeInsetsMake( 0.0, -image.size.width, kTextBottomOffset,  0.0)]; 

    [button setImage: (UIImage*)       image 
            forState: (UIControlState) buttonState ];

    [button setImageEdgeInsets: UIEdgeInsetsMake( kImageTopOffset, 0.0, 0.0,- button.titleLabel.bounds.size.width)];
}
Bamaco
fuente
2

O simplemente puede usar esta categoría:

@interface UIButton (VerticalLayout)  

- (void)centerVerticallyWithPadding:(float)padding;  
- (void)centerVertically;  

@end  


@implementation UIButton (VerticalLayout)  

- (void)centerVerticallyWithPadding:(float)padding 
{      
    CGSize imageSize = self.imageView.frame.size;  
    CGSize titleSize = self.titleLabel.frame.size;  

    CGFloat totalHeight = (imageSize.height + titleSize.height + padding);  

    self.imageEdgeInsets = UIEdgeInsetsMake(- (totalHeight - imageSize.height),
                                            0.0f,
                                            0.0f,
                                            - titleSize.width);

    self.titleEdgeInsets = UIEdgeInsetsMake(0.0f,
                                            - imageSize.width,
                                            - (totalHeight - titleSize.height),
                                            0.0f);

}


- (void)centerVertically
{  
    const CGFloat kDefaultPadding = 6.0f;

    [self centerVerticallyWithPadding:kDefaultPadding];  
}  


@end
RaffAl
fuente
1
Esto no funcionaría con una fuente personalizada. Desde iOS7 +,CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName: button.titleLabel.font}];
Hemang
1

Mi caso de uso hizo insertos inmanejables:

  1. la imagen de fondo en el botón se mantiene constante
  2. El texto dinámico y la imagen cambian donde la longitud de la cadena y el tamaño de la imagen varían

Esto es lo que terminé haciendo y estoy bastante feliz con eso:

  • Cree el botón en el guión gráfico con una imagen de fondo (círculo redondo con desenfoque y color).

  • Declarar un UIImageView en mi clase:

    @implementation BlahViewController {
        UIImageView *_imageView;
    }
  • Crear instancia de vista de imagen en init:

    -(id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if (self) {
            _imageView = [[UIImageView alloc] initWithCoder:aDecoder];
         }
         return self;
     }
  • En viewDidLoad agregue una nueva capa al botón para nuestra vista de imagen y configure la alineación del texto:

    [self.btn addSubview:_imageView];
    [self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter];
  • En el método de hacer clic en el botón, agregue mi imagen de superposición elegida a la vista de imagen, ajústela para que se ajuste a la imagen y céntrela en el botón, pero muévala hacia arriba 15 para que pueda colocar el texto debajo:

    [_imageView setImage:[UIImage imageNamed:@"blahImageBlah]];
    [_imageView sizeToFit];
    _imageView.center = CGPointMake(ceilf(self.btn.bounds.size.width / 2.0f),
             ceilf((self.btn.bounds.size.height / 2.0f) - 15));
    [self.btn setTitle:@"Some new text" forState:UIControlStateNormal];

Nota: ceilf () es importante para garantizar que esté en un límite de píxeles para la calidad de la imagen.

Oliver Dungey
fuente
1
Definitivamente un mejor enfoque para mi caso de uso, ya que estoy agregando el botón a una vista de pila.
valeCocoa
0

Suponiendo que desea que tanto el texto como la imagen estén centrados horizontalmente, imagen arriba del texto: Centre el texto desde el generador de interfaces y agregue un recuadro superior (dejando espacio para la imagen). (deje el recuadro izquierdo a 0). Utilice el generador de interfaces para elegir la imagen: su posición real se establecerá a partir del código, así que no se preocupe de que las cosas no se verán bien en IB. A diferencia de otras respuestas anteriores, esto realmente funciona en todas las versiones de iOS actualmente compatibles (5,6 y 7).

En el código, simplemente descarte el ImageView del botón (configurando la imagen del botón como nulo) después de tomar la imagen (esto también centrará automáticamente el texto, envuelto si es necesario). Luego, cree una instancia de su propio ImageView con el mismo tamaño de cuadro e imagen y colóquelo en el medio.

De esta manera, aún puede elegir la imagen del generador de interfaces (aunque no estará alineada en IB como en el simulador, pero, de nuevo, otras soluciones no son compatibles en todas las versiones compatibles de iOS)

Radu Simionescu
fuente
0

Estaba luchando para lograr esto porque no podía obtener el tamaño de la imagen y el ancho del texto en el constructor de mi vista. Dos cambios menores en la respuesta de Jesse funcionaron para mí:

CGFloat spacing = 3;
self.titleEdgeInsets = UIEdgeInsetsMake(0.0, - image.size.width, - (image.size.height + spacing), 0.0);
CGSize titleSize = [name sizeWithAttributes:@{NSFontAttributeName:self.titleLabel.font}];
self.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);

Los cambios son:

  • Utilizando [NSString sizeWithAttributes] para obtener el ancho del texto;
  • Obtenga el tamaño de la imagen directamente en UIImagelugar deUIImageView
saulobrito
fuente
0

Esto funciona bien para mí, para varios botones, con diferente ancho de imagen y diferente longitud de título:

Subclase UIButton

override func layoutSubviews() {
    super.layoutSubviews()

    if let image = imageView?.image {

        let margin = 30 - image.size.width / 2
        let titleRect = titleRectForContentRect(bounds)
        let titleOffset = (bounds.width - titleRect.width - image.size.width - margin) / 2


        contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
            imageEdgeInsets = UIEdgeInsetsMake(0, margin, 0, 0)
            titleEdgeInsets = UIEdgeInsetsMake(0, (bounds.width - titleRect.width -  image.size.width - margin) / 2, 0, 0)
    }

}
Rémy Virin
fuente
0

Funciona bien para el tamaño del botón 80x80 píxeles.

[self.leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, 10.0, 20.0, 10.0)];    
[self.leftButton setTitleEdgeInsets:UIEdgeInsetsMake(60, -75.0, 0.0, 0.0)];
BADRI
fuente
0

Realicé algunos ajustes para alinear la imagen en el centro horizontal:

// the space between the image and text
        let spacing = CGFloat(36.0);

        // lower the text and push it left so it appears centered
        //  below the image
        let imageSize = tutorialButton.imageView!.frame.size;
        tutorialButton.titleEdgeInsets = UIEdgeInsetsMake(
            0, -CGFloat(imageSize.width), -CGFloat(imageSize.height + spacing), 0.0);

        // raise the image and push it right so it appears centered
        //  above the text
        let titleSize = tutorialButton.titleLabel!.frame.size;
        tutorialButton.imageEdgeInsets = UIEdgeInsetsMake(
            -CGFloat(titleSize.height + spacing), CGFloat((tutorialButton.frame.width - imageSize.width) / 2), 0.0, -CGFloat(titleSize.width));
Nguyễn Thanh Khiêm
fuente
0

¿Es obligatorio usar inserciones de borde? Si no, puede intentar posicionar respecto a la vista central del padre

extension UIButton 
{
    func centerImageAndTextVerticaAlignment(spacing: CGFloat) 
    {
        var titlePoint : CGPoint = convertPoint(center, fromView:superview)
        var imageViewPoint : CGPoint = convertPoint(center, fromView:superview)
        titlePoint.y += ((titleLabel?.size.height)! + spacing)/2
        imageViewPoint.y -= ((imageView?.size.height)! + spacing)/2
        titleLabel?.center = titlePoint
        imageView?.center = imageViewPoint

    }
}
Rodrigo Birriel
fuente
La pregunta pide explícitamente el uso de imageEdgeInsets y titleEdgeInsets, por lo que es probable que sea obligatorio
Tibrogargan
0

Debe mover la imagen hacia la derecha por el ancho del texto. Luego mueva el texto a la izquierda por el ancho de la imagen.

UIEdgeInsets imageEdgeInsets = self.remoteCommandsButtonLights.imageEdgeInsets;
imageEdgeInsets.left = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName:[button.titleLabel font]}].width;
imageEdgeInsets.bottom = 14.0;
button.imageEdgeInsets = imageEdgeInsets;

UIEdgeInsets titleEdgeInsets = self.remoteCommandsButtonLights.titleEdgeInsets;
titleEdgeInsets.left = -button.currentImage.size.width;
titleEdgeInsets.top = 20.0;
button.titleEdgeInsets = titleEdgeInsets;

Luego ajuste las inserciones superior e inferior para ajustar el eje Y. Es probable que esto también se pueda hacer mediante programación, pero debe ser constante para el tamaño de su imagen. Mientras que las inserciones del eje X deberán cambiar en función del tamaño de la etiqueta de texto en cada botón.

pkamb
fuente
0

Agregue este código en la extensión Swift 4.2

 func moveImageLeftTextCenter(imagePadding: CGFloat = 30.0){
    guard let imageViewWidth = self.imageView?.frame.width else{return}
    guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
    self.contentHorizontalAlignment = .left
    imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imagePadding - imageViewWidth / 2, bottom: 0.0, right: 0.0)
    titleEdgeInsets = UIEdgeInsets(top: 0.0, left: (bounds.width - titleLabelWidth) / 2 - imageViewWidth, bottom: 0.0, right: 0.0)
}
func moveImageRIghtTextCenter(imagePadding: CGFloat = 30.0){
    guard let imageViewWidth = self.imageView?.frame.width else{return}
    guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
    self.contentHorizontalAlignment = .right
    imageEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right: imagePadding - imageViewWidth / 2)
    titleEdgeInsets = UIEdgeInsets(top: 0.0, left:0.0 , bottom: 0.0, right:(bounds.width - titleLabelWidth) / 2 - imageViewWidth)
}
Shahzaib Maqbool
fuente
0

Solo para tirar mis 2 centavos, esto funcionó para mí:

extension UIButton {
  public func centerImageAndTextVertically(spacing: CGFloat) {
    layoutIfNeeded()
    let contentFrame = contentRect(forBounds: bounds)
    let imageFrame = imageRect(forContentRect: contentFrame)
    let imageLeftInset = bounds.size.width * 0.5 - imageFrame.size.width * 0.5
    let imageTopInset = -(imageFrame.size.height + spacing * 0.5)
    let titleFrame = titleRect(forContentRect: contentFrame)
    let titleLeftInset = ((bounds.size.width - titleFrame.size.width) * 0.5) - imageFrame.size.width
    let titleTopInmset = titleFrame.size.height + spacing * 0.5
    imageEdgeInsets = UIEdgeInsets(top: imageTopInset, left: imageLeftInset, bottom: 0, right: 0)
    titleEdgeInsets = UIEdgeInsets(top: titleTopInmset, left: titleLeftInset, bottom: 0, right: 0)
  }
}
Chris Wooden
fuente