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.