UIButton: как центрировать изображение и текст с помощью imageEdgeInsets и titleEdgeInsets?
Если я помещаю только изображение в кнопку и устанавливаю imageEdgeInsets ближе к вершине, изображение остается центрированным, и все работает так, как ожидалось:
[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)];
Если я помещаю только текст в кнопку и устанавливаю titleEdgeInsets ближе к нижней части, текст остается центрированным, и все работает так, как ожидалось:
[button setTitle:title forState:UIControlStateNormal];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)];
но, если я помещу 4 строки вместе, текст будет мешать изображению, и оба потеряют выравнивание по центру.
все мои изображения имеют ширину 30 пикселей, и если я помещаю 30 в левый параметр UIEdgeInsetMake для setTitleEdgeInsets, текст снова центрируется. Проблема в том, что изображение никогда не центрируется, потому что кажется, что оно зависит от кнопки.размер titleLabel. Я уже пробовал много вычислений с размером кнопки, размером изображения, размером заголовка и никогда не получал оба идеально центрированных.
у кого-то уже была такая же проблема?
24 ответов:
для чего это стоит, вот общее решение для позиционирования изображения по центру над текстом без использования каких-либо магических чисел. обратите внимание, что следующий код устарел, и вы, вероятно, должны использовать одну из обновленных версий ниже:
// 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);следующая версия содержит изменения для поддержки iOS 7+, которые были рекомендованы в комментариях ниже. Я не проверял этот код, поэтому я не уверен, насколько хорошо он работает или будет ли он перерыв, если используется в предыдущих версиях 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);Свифт версия
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) } }
нашел, как.
во-первых, настроить текст
titleLabel(из-за стилей, т. е. полужирный, курсив и т.д.). Затем используйтеsetTitleEdgeInsetsучитывая ширину вашего изображения:[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)];после этого, используйте
setTitleEdgeInsetsучитывая ширину границ текста:[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)];теперь изображение и текст будут центрированы (в этом примере изображение появляется над текстом).
ваше здоровье.
вы можете сделать это с помощью этого расширения Swift, которое было частично основано на ответе Джесси Кроссена:
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) } }Это определяет функцию
centerLabelVerticallyWithPaddingчто устанавливает заголовок и изображения вставок соответствующим образом.Он также устанавливает contentEdgeInsets, который я считаю необходимым, чтобы гарантировать, что
intrinsicContentSizeпо-прежнему работает правильно, что бы использовать автоматическую компоновку.Я считаю, что все решения, которые подкласс UIButton технически незаконны, так как вы не являетесь должен подкласс элементы программирования с использованием UIKit. То есть, теоретически они могут сломаться в будущих выпусках.
изменить: обновлено для Swift 3
в случае, если вы ищете быстрое решение ответа Джесси Кроссена, вы можете добавить это в подкласс 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() }
есть много примеров здесь, но я не мог заставить это работать во всех случаях, когда имеем дело с несколькими строками текста (Обтекание текстом). Чтобы, наконец, заставить его работать я объединил несколько методов:
я использовал пример Джесси Кроссена выше. Тем не менее, я исправил высоту текста вопрос и я добавил возможность указать горизонтальное поле текста. Поле полезно, когда текст можно обернуть, чтобы он не попал по краю кнопка:
// 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убедитесь, что вы настроили текстовую метку для обертывания
button.titleLabel.numberOfLines = 2; button.titleLabel.lineBreakMode = UILineBreakModeWordWrap; button.titleLabel.textAlignment = UITextAlignmentCenter;Это будет в основном работать. Однако у меня были некоторые кнопки, которые не отображали их изображение правильно. Изображение было либо сдвинуто далеко вправо, либо влево (оно не было центрировано). Поэтому я использовал метод переопределения макета UIButton, чтобы заставить 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
Я сделал метод для ответа @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); }
Не боритесь с системой. Если ваши макеты становятся слишком сложными для управления с помощью Interface Builder + возможно, какой-то простой код конфигурации, сделайте макеты вручную более простым способом с помощью
layoutSubviews- вот для чего это! Все остальное будет равносильно взломам.создайте подкласс UIButton и переопределите его
layoutSubviewsметод для выравнивания текста и изображения программно. Или использовать что-то вроде https://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.h таким образом, вы можете реализовать layoutSubviews с помощью блока.
Подкласс 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); }
ничего плохого в других ответах, однако я просто хотел отметить, что то же самое поведение может быть выполнено визуально в Xcode с использованием нулевых строк кода. Это решение полезно, если вам не нужно вычисленное значение или вы строите с помощью раскадровки/xib (в противном случае применяются другие решения).
примечание - Я понимаю, что вопрос OP является одним из требующих кода. Я просто предоставляю этот ответ для полноты и в качестве логической Альтернативы для тех, кто использует раскадровки/xibs.
для изменения интервала между изображениями, заголовками и представлениями содержимого кнопки с помощью краевых вставок можно выбрать кнопку / элемент управления и открыть инспектор атрибутов. Прокрутите вниз до середины инспектора и найдите раздел для краевых вставок.
можно также получить доступ и изменить определенные вставки края для заголовка, изображения или представления содержимого .
расширение UIButton с 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) } }оригинальный ответ:https://stackoverflow.com/a/7199529/3659227
просто небольшое изменение в ответе Джесси Кроссена, которое заставило его отлично работать для меня:
вместо:
CGSize titleSize = button.titleLabel.frame.size;я использовал этот:
CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}];
С этим куском кода, Вы получите что-то вроде этого
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) } }
используя
button.titleLabel.frame.size.widthотлично работает только до тех пор, пока метка достаточно коротка, чтобы не быть усеченной. Однако, когда текст метки усекается, позиционирование не работает. ПринимаяCGSize titleSize = [[[button titleLabel] text] sizeWithFont:[[button titleLabel] font]];работает для меня, даже когда текст усекается.
Я посмотрел на существующие ответы, но я также обнаружил, что установка рамки кнопки является важным первым шагом.
вот функция, которую я использую это:
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)]; }
или вы можете просто использовать эту категорию:
@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
мой случай использования сделал вставки неуправляемыми:
- фоновое изображение на кнопке остается последовательным
- динамические изменения текста и изображения, где длина строки и размер изображения изменяется
это то, что я закончил делать, и я очень доволен этим:
создайте кнопку на раскадровке с фоновым изображением (круглый круг с размытием и цветом).
объявить UIImageView в моем класс:
@implementation BlahViewController { UIImageView *_imageView; }создать экземпляр представления изображения на init:
-(id)initWithCoder:(NSCoder *)aDecoder { self = [super initWithCoder:aDecoder]; if (self) { _imageView = [[UIImageView alloc] initWithCoder:aDecoder]; } return self; }в viewDidLoad добавьте новый слой к кнопке для нашего вида изображения и установите выравнивание текста:
[self.btn addSubview:_imageView]; [self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter];в методе нажатия кнопки добавьте выбранное изображение наложения в представление изображения, размер его, чтобы соответствовать изображению и центрировать его в кнопке, но переместите его вверх 15, чтобы я мог поместить смещение текста под ним:
[_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];Примечание.: ceilf () важно обеспечить его на границе пикселя для качества изображения.
обновил ответ Джесси Кроссена на 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) } }использовать таким образом:
override func viewDidLayoutSubviews() { button.alignVertical() }
предполагая, что вы хотите, чтобы текст и изображение были центрированы по горизонтали, изображение над текстом: Центрируйте текст из interface builder и добавьте верхнюю вставку (освободив место для изображения). (оставьте левую вставку в 0). Используйте interface builder для выбора изображения-это фактическое положение будет установлено из кода, поэтому не волнуйтесь, что все не будет выглядеть нормально В IB. В отличие от других ответов выше, это фактически работает на всех поддерживаемых в настоящее время версиях ios (5,6 и 7).
в коде, как раз сбросьте ImageView кнопки (установив изображение кнопки в null) после захвата изображения (это также автоматически центрирует текст, обернутый при необходимости). Затем создайте свой собственный ImageView с тем же размером кадра и изображения и поместите его в середине.
таким образом, вы все еще можете выбрать изображение из interface builder (хотя он не будет выровнен В IB, как в симуляторе, но опять же, другие решения не совместимы во всех поддерживаемых версиях ios)
Я изо всех сил пытался сделать это, потому что я не мог получить размер изображения и ширину текста в конструкторе моего представления. Два незначительных изменения на Джесси работала на меня:
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);изменения:
- используя
[NSString sizeWithAttributes]чтобы получить ширину текста;- получить размер изображения непосредственно на
UIImageвместоUIImageView
это хорошо работает для меня, для нескольких кнопок, с разной шириной изображения и разной длиной заголовка:
подкласс
UIButtonoverride 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) } }
работает нормально для размера кнопки 80x80 пикселей.
[self.leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, 10.0, 20.0, 10.0)]; [self.leftButton setTitleEdgeInsets:UIEdgeInsetsMake(60, -75.0, 0.0, 0.0)];
Я сделал некоторые настройки, чтобы сделать изображение выровненным по центру горизонтально:
// 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));
обязательно ли использовать кромку вставки? Если нет, вы можете попытаться расположить уважение к центральному родительскому виду
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 } }
вам нужно переместить изображение вправо по ширине текста. Затем переместите текст влево по ширине изображения.
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;затем отрегулируйте верхнюю и нижнюю вставки для регулировки оси Y. Это, вероятно, может быть сделано программно, но должно быть постоянным для вашего размера изображения. В то время как вставки по оси X необходимо будет изменить в зависимости от размера текстовой метки в каждой кнопке.



Comments