titlelabel guidelines color buttons ios objective-c uibutton

ios - guidelines - UIButton: cómo centrar una imagen y un texto usando imageEdgeInsets y titleEdgeInsets?



menu ios (23)

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

[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 coloco titleEdgeInsets más cerca de la parte inferior, el texto permanece centrado y todo funciona como se espera:

[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 vuelve a centrarse. 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 del título y nunca los puse perfectamente centrados.

¿Alguien ya tenía el mismo problema?


¿Es obligatorio usar bordes de inserción? Si no, puede intentar posicionar el respeto en la vista central de los padres

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


Aquí hay algunos buenos ejemplos, pero no pude lograr que funcionen en todos los casos cuando también se trata de varias líneas de texto (ajuste de texto). Para finalmente hacerlo funcionar, combiné algunas de las técnicas:

  1. Usé el ejemplo de Jesse Crossen arriba. Sin embargo, arreglé un problema de altura de texto y agregué la capacidad de especificar un margen de texto horizontal. El margen es útil al permitir 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 que se ajuste

    button.titleLabel.numberOfLines = 2; button.titleLabel.lineBreakMode = UILineBreakModeWordWrap; button.titleLabel.textAlignment = UITextAlignmentCenter;

  3. Esto funcionará mayormente ahora. Sin embargo, tenía algunos botones que no representarían 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 de UIButton para forzar que el imageView esté centrado.

    @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


Con este trozo de código, obtendrás algo como esto

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


Editar: Actualizado para Swift 3

En caso de que esté buscando una solución Swift de la respuesta de Jesse Crossen, puede agregarla 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() }


El uso de button.titleLabel.frame.size.width funciona bien solo si la etiqueta es lo suficientemente corta como para no ser truncada. 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.


Encontrado como.

Primero, configure el texto de titleLabel (debido a los estilos, es decir, negrita, cursiva, etc.). Luego, use setTitleEdgeInsets teniendo en cuenta 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 setTitleEdgeInsets teniendo en cuenta el ancho de los límites de 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).

Aclamaciones.


Estaba luchando por hacer esto porque no pude obtener el tamaño de la imagen y el ancho del texto en el constructor de mi vista. Dos pequeños cambios 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);

El cambio es:

  • Usar [NSString sizeWithAttributes] para obtener el ancho del texto;
  • Obtener tamaño de imagen directamente en el UIImage lugar de UIImageView

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


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://.com/a/7199529/3659227


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


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


Mi caso de uso hizo que las inserciones fueran inmanejables:

  1. la imagen de fondo en el botón permanece consistente
  2. cambios dinámicos de texto e imagen donde la longitud de la cuerda y el tamaño de la imagen varían

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

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

  • Declare 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 agrega una nueva capa al botón para nuestra vista de imagen y establece la alineación del texto:

    [self.btn addSubview:_imageView]; [self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter];

  • En el método de clic de botón, agregue la imagen de superposición elegida a la vista de imagen, mida el tamaño para que se ajuste a la imagen y céntrela en el botón, pero muévala hacia arriba 15 para que pueda poner el desplazamiento de texto debajo de ella:

    [_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 asegurarse de que está en un límite de píxeles para la calidad de imagen.


Miré las respuestas existentes, pero también encontré que establecer el marco del botón es un primer paso importante.

Aquí hay una función que uso que se ocupa 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)]; }


Necesita mover la imagen a la derecha por el ancho del texto. A continuación, mueva el texto hacia 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. Esto también podría hacerse programáticamente, pero debería ser constante para su tamaño de 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.


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 está construyendo con un guión gráfico / xib (de lo contrario, se aplican otras soluciones).

Nota: entiendo que la pregunta del OP es un código que requiere. Solo estoy brindando esta respuesta para que esté completa y como una alternativa lógica para quienes usan storyboards / xibs.

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

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


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

Cree una subclase UIButton y anule su método layoutSubviews 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.


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


Por lo que vale, aquí hay una solución general para posicionar la imagen centrada sobre el texto sin usar ningún número mágico. Tenga en cuenta que el siguiente código no está actualizado y probablemente debería utilizar 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á si se usa 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

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(attributes: [NSFontAttributeName: 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) } }


Puedes 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 apropiadamente las inserciones de título e imagen.

También establece contentEdgeInsets, que creo que es necesario para garantizar que intrinsicContentSize todavía funcione correctamente, lo que necesitaría usar Auto Layout.

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


Realicé algunos ajustes para que la imagen se alineara 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));


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

en lugar de:

CGSize titleSize = button.titleLabel.frame.size;

Yo he usado esto:

CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}];


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


Suponiendo que desea que tanto el texto como la imagen se centren horizontalmente, imagen arriba del texto: Centre el texto del constructor de interfaz y agregue un recuadro superior (dejando espacio para la imagen). (deja el recuadro de la izquierda a 0). Utilice el constructor de interfaz para elegir la imagen; su posición actual se establecerá a partir del código, por lo que no debe preocuparse de que las cosas no se vean bien en IB. A diferencia de otras respuestas anteriores, esto realmente funciona en todas las versiones ios soportadas actualmente (5,6 y 7).

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

De esta forma, puede elegir la imagen desde el constructor de la interfaz (aunque no estará alineado en IB como en el simulador, pero, de nuevo, otras soluciones no son compatibles en todas las versiones compatibles de ios)