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 і ніколи не отримую обидва ідеально по центру.

У когось вже була така ж проблема?

Відповіді:


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

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

Ура.


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все-таки працює правильно, для чого потрібно буде використовувати автоматичний макет.

Я вважаю, що всі рішення, які мають підклас 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
— brockhampton,

@brockhampton - дивіться оновлену відповідь на нове місце
— Tommie C.

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

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

Через багато-багато годин. Це було єдине, що спрацювало :)
— Гаррі Блакитний

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

Ласкаво просимо до SO! Замість того, щоб додавати окрему відповідь (для незначної зміни), ви можете написати це безпосередньо Джессі, щоб він міг перевірити та оновити свою [прийняту] відповідь правильно (якщо потрібно).
— Хеман

3

Використання button.titleLabel.frame.size.widthпрацює добре лише до тих пір, поки мітка досить коротка, щоб не бути усіченою. Коли текст етикетки стає усіченим, позиціонування не працює. Приймаючи

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

працює для мене, навіть коли текст мітки урізаний.


у вас помилка ботонів.
— Бхімбім

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

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

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). Використовуйте конструктор інтерфейсів для вибору зображення - його фактичне положення буде встановлено з коду, тому не хвилюйтеся, що в ІБ все не буде виглядати нормально. На відміну від інших відповідей вище, це фактично працює на всіх підтримуваних на даний момент версіях 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

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

Добре працює для розміру кнопки розміром 80x80 пікселів.

[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 потрібно змінювати, виходячи з розміру текстової мітки в кожній кнопці.


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)
  }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.