UIButton: как центрировать изображение и текст, используя imageEdgeInsets и titleEdgeInsets?

159

Если я помещу в изображение только изображение и установлю 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, текст снова будет центрирован. Проблема в том, что изображение никогда не центрируется, потому что кажется, что оно зависит от размера button.titleLabel. Я уже пробовал много вычислений с размером кнопки, размером изображения, размером titleLabel и никогда не получал идеальное центрирование.

У кого-то уже была такая же проблема?

reinaldoluckman
источник

Ответы:

412

Для чего это стоит, вот общее решение для размещения изображения по центру над текстом без использования каких-либо магических чисел. Обратите внимание, что следующий код устарел, и вам, вероятно, следует использовать одну из обновленных версий ниже :

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

Версия 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
    )
  }
}
Джесси Кроссен
источник
5
Замечательно - спасибо! Я думаю, что многие другие ответы идут об этом "трудном пути" - это выглядит намного лучше.
Джо д'Андреа
3
Я нашел вышеупомянутое в работе, но у меня нет абсолютно никакой ментальной модели для того, как. Кто-нибудь получил ссылку на графическое объяснение того, на что влияют отдельные параметры EdgeInsets? И почему ширина текста изменилась?
Роберт Аткинс
3
Это не работает, когда изображение сокращается до размера кнопки. Похоже, что UIButton (по крайней мере, в iOS 7) использует image.size, а не imageView.frame.size для своих центрирующих вычислений.
Дэн Джексон
5
@ Hemang и Дэн Джексон, я включаю ваши предложения, не проверяя их самостоятельно. Я нахожу несколько смешным, что изначально написал это для iOS 4, и после стольких версий нам все еще приходится существенно перепроектировать алгоритм компоновки Apple, чтобы получить такую ​​очевидную особенность. Или, по крайней мере, я предполагаю, что нет лучшего решения из последовательного потока голосов и одинаково хакерских ответов ниже (никакого оскорбления не предполагается).
Джесси Кроссен
5
В iOS8 я нашел, что лучше использовать button.currentTitleвместо того, button.titleLabel.textособенно если текст кнопки когда-либо изменится. currentTitleсразу же заселяется, в то время как изменения titleLabel.textмогут происходить медленно, что может привести к смещению вставок.
mjangda
59

Нашел как.

Сначала настройте текст 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)];

Теперь изображение и текст будут центрированы (в этом примере изображение появляется над текстом).

Приветствия.

reinaldoluckman
источник
3
7 лет, мужик. На самом деле не помню. Когда я спросил, я ответил сам (мой ответ был единственным в то время). Я изменил выбранный ответ, когда автор текущего выбранного ответа сосредоточился на устранении этих магических чисел. Таким образом, в выбранном ответе вы можете понять, что они значат.
reinaldoluckman
20

Вы можете сделать это с помощью этого расширения 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правильной работы, для чего необходимо использовать Auto Layout.

Я считаю, что все решения, которые подкласс UIButton, технически незаконны, так как вы не должны подкласс UIKit управления. То есть теоретически они могут сломаться в будущих выпусках.

водорослевые
источник
Тестирование на iOS9. Изображение выглядит по центру, но текст отображается слева :(
endavid
13

Редактировать: Обновлено для 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()
}
Томас Вербик
источник
9

Здесь есть несколько замечательных примеров, но я не мог заставить это работать во всех случаях, когда также имел дело с несколькими строками текста (перенос текста). Чтобы наконец заставить его работать, я объединил несколько приемов:

  1. Я использовал пример Джесси Кроссен выше. Однако я исправил проблему с высотой текста и добавил возможность указать горизонтальное поле для текста. Поле полезно для переноса текста, чтобы оно не касалось края кнопки:

    // 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. Убедитесь, что вы установили текстовую метку для переноса

    button.titleLabel.numberOfLines = 2; 
    button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
    button.titleLabel.textAlignment = UITextAlignmentCenter;
  3. Это будет в основном работать сейчас. Тем не менее, у меня были кнопки, которые не отображали их изображение правильно. Изображение было смещено далеко вправо или влево (не по центру). Поэтому я использовал технику переопределения макета 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
Тод Каннингем
источник
Похоже, это хорошее решение, однако не должен ли button.titleLabel.numberOfLines быть 0 таким, чтобы в нем могло быть столько строк, сколько нужно?
Бен Лахман
В моем случае я хотел только до двух строк. В противном случае изображение будет иметь проблемы с общим размером кнопки.
Тод Каннингем
9

Я сделал метод для ответа @ 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);
 }
Бекир Онат Акин
источник
7

Обновлено для Xcode 11+

Вставки, описанные в моем первоначальном ответе, были перемещены в инспектор размера в более поздних версиях Xcode. Я не уверен на 100%, когда произошло переключение, но читатели должны просмотреть инспектор размера, если информация о вставке не найдена в инспекторе атрибутов. Ниже приведен пример нового экрана вставки (расположен в верхней части инспектора атрибутов размера по состоянию на 11.5).

insets_moved_to_size_inspector

Оригинальный ответ

В других ответах нет ничего плохого, однако я просто хотел отметить, что такое же поведение можно выполнить визуально в XCode, используя ноль строк кода. Это решение полезно, если вам не нужно рассчитанное значение или вы строите с раскадровкой / XIB (в противном случае применяются другие решения).

Примечание. Я понимаю, что вопрос ОП требует кода. Я просто предоставляю этот ответ для полноты и в качестве логической альтернативы для тех, кто использует раскадровки / XIBS.

Чтобы изменить интервал на изображении, заголовке и представлении содержимого кнопки с помощью вставок ребер, вы можете выбрать кнопку / элемент управления и открыть инспектор атрибутов. Прокрутите вниз к середине инспектора и найдите раздел для вставок Edge.

кромочные-вставки

Можно также получить доступ и изменить определенные граничные вставки для заголовка, изображения или просмотра содержимого.

меню-опции

Томми С.
источник
Я не понимаю, почему я не могу ввести отрицательные числа в раскадровку для некоторых значений.
Даниэль Т.
Это работает для более новой версии Swift? Я нигде не могу найти атрибут Edge
Брокхемптон
@brockhampton - см. обновленный ответ для нового местоположения
Томми С.
6

Не борись с системой. Если ваши макеты становятся слишком сложными для управления с помощью Interface Builder +, возможно, некоторого простого кода конфигурации, сделайте макеты вручную более простым способом layoutSubviews- вот для чего он! Все остальное будет равносильно взлому.

Создайте подкласс UIButton и переопределите его layoutSubviewsметод для программного выравнивания текста и изображения. Или используйте что-то вроде https://github.com/nickpaulson/BlockKit/blob/master/Source/UIView-BKAdditions.h, чтобы вы могли реализовать layoutSubviews, используя блок.

Стивен Крамер
источник
6

Подкласс 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
источник
6

Обновленный ответ Джесси Кроссена для 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()
}
Doci
источник
Через много-много часов. Это было единственное, что сработало :)
Harry Blue
4

С этим фрагментом кода вы получите что-то вроде этого выравнивание заголовка и изображения

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)
    }
}
Богдан Савыч
источник
2
Я написал небольшое расширение для размещения изображения и текста внутри кнопки. Если вы заинтересованы, вот исходный код. github.com/sssbohdan/ButtonAlignmentExtension/blob/master/…
Богдан Савыч
4

Расширение 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

инакомыслящий
источник
3

Просто небольшое изменение в ответе Джесси Кроссена, которое сделало его идеальным для меня:

вместо того:

CGSize titleSize = button.titleLabel.frame.size;

Я использовал это:

CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName:button.titleLabel.font}];
Cesar
источник
Добро пожаловать на ТАК! Вместо добавления отдельного ответа (для незначительного изменения) вы можете написать это Джесси напрямую, чтобы он мог проверить и обновить свой [принятый] ответ правильно (при необходимости).
Hemang
3

Использование button.titleLabel.frame.size.widthработает нормально только до тех пор, пока метка достаточно короткая, чтобы ее нельзя было усечь. Когда текст метки становится усеченным, позиционирование не работает. Взятие

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

работает для меня, даже когда текст метки обрезан.

Dezember
источник
у вас есть опечатка.
Бхимбим
2

Я посмотрел на существующие ответы, но также обнаружил, что установка фрейма кнопки - важный первый шаг.

Вот функция, которую я использую, которая заботится об этом:

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
источник
2

Или вы можете просто использовать эту категорию:

@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
источник
1
Это не будет работать с пользовательским шрифтом. От iOS7 +,CGSize titleSize = [button.titleLabel.text sizeWithAttributes: @{NSFontAttributeName: button.titleLabel.font}];
Hemang
1

Мой случай использования сделал вставки неуправляемыми:

  1. фоновое изображение на кнопке остается неизменным
  2. динамический текст и изображение изменяются в зависимости от длины строки и размера изображения

Это то, что я в итоге сделал, и я очень доволен этим:

  • Создайте кнопку на раскадровке с фоновым изображением (круглый круг с размытием и цветом).

  • Объявите 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 () важен для обеспечения качества изображения на границе пикселей.

Оливер Данджи
источник
1
Определенно лучший подход для моего варианта использования, так как я добавляю кнопку в представление стека.
valeCocoa
0

Предполагая, что вы хотите, чтобы текст и изображение были центрированы по горизонтали, изображение над текстом: отцентрируйте текст в конструкторе интерфейса и добавьте верхнюю вставку (освобождая место для изображения). (оставьте левую вставку в 0). Используйте конструктор интерфейса, чтобы выбрать изображение - его фактическое положение будет установлено из кода, поэтому не беспокойтесь, что в IB все будет выглядеть не очень хорошо. В отличие от других ответов выше, это на самом деле работает на всех поддерживаемых в настоящее время версиях ios (5,6 и 7).

В коде просто отмените ImageView кнопки (установив изображение кнопки на ноль) после захвата изображения (это также автоматически центрирует текст, при необходимости оборачивая). Затем создайте свой собственный ImageView с тем же размером кадра и изображением и поместите его посередине.

Таким образом, вы все равно можете выбрать образ из конструктора интерфейса (хотя он не будет выровнен в IB, как в симуляторе, но опять же, другие решения несовместимы во всех поддерживаемых версиях ios)

Раду Симионеску
источник
0

Я изо всех сил пытался сделать это, потому что я не мог получить размер изображения и ширину текста в конструкторе моего представления. У меня сработали два небольших изменения в ответе Джесси :

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
saulobrito
источник
0

Это хорошо работает для меня, для нескольких кнопок, с разной шириной изображения и разной длиной заголовка:

Подкласс 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)
    }

}
Реми Вирин
источник
0

Работает нормально для кнопки размером 80х80 пикселей.

[self.leftButton setImageEdgeInsets:UIEdgeInsetsMake(0, 10.0, 20.0, 10.0)];    
[self.leftButton setTitleEdgeInsets:UIEdgeInsetsMake(60, -75.0, 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));
Нгуен Тхань Кием
источник
0

обязательно ли использовать краевые вставки? Если нет, вы можете попытаться позиционировать уважение к центру родительского представления

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

    }
}
Родриго Бирриэль
источник
Вопрос явно требует использования imageEdgeInsets и titleEdgeInsets, поэтому вполне вероятно, что это обязательно
Tibrogargan
0

Вам нужно переместить изображение вправо по ширине текста. Затем переместите текст влево на ширину изображения.

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 необходимо будет изменить в зависимости от размера текстовой метки в каждой кнопке.

pkamb
источник
0

Добавьте этот код в расширение 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)
}
Шахзаиб Макбул
источник
0

Просто скинуть мои 2 цента, это сработало для меня:

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)
  }
}
Крис Вуден
источник