Вирівнювання тексту та зображення на UIButton з imageEdgeInsets та titleEdgeInsets


249

Я хотів би розмістити піктограму зліва від двох рядків тексту таким чином, щоб між зображенням та початком тексту було близько 2-3 пікселів. Сам елемент керування розташований у центрі горизонтально (встановлюється через Interface Builder)

Кнопка нагадує щось подібне:

|                  |
|[Image] Add To    |
|        Favorites |

Я намагаюся налаштувати це з contentEdgeInset, imageEdgeInsets і titleEdgeInsets безрезультатно. Я розумію, що від'ємне значення розширює край, тоді як додатне значення зменшує його, щоб перемістити його ближче до центру.

Я намагався:

[button setTitleEdgeInsets:UIEdgeInsetsMake(0, -image.size.width, 0, 0)];
[button setImageEdgeInsets:UIEdgeInsetsMake(0, button.titleLabel.bounds.size.width, 0, 0)];

але це не відображається правильно. Я налаштовував значення, але перехід від скажіть -5 до -10 на лівому введеному значенні не здається, щоб перемістити його очікуваним чином. -10 почерпне текст увесь ліворуч, тому я очікував, що -5 почне його ліворуч, але це не так.

Яка логіка за вставками? Я не знайомий з розміщенням зображень та відповідною термінологією.

Я використав це питання ТА як орієнтир, але щось про мої цінності не правильно. UIButton: як центрувати зображення та текст за допомогою imageEdgeInsets та titleEdgeInsets?

Відповіді:


392

Я погоджуюся з документацією imageEdgeInsetsі titleEdgeInsetsповинна бути кращою, але я зрозумів, як отримати правильне позиціонування, не вдаючись до спроб та помилок.

Загальна ідея тут у цьому питанні , але це було так, якби ви хотіли, щоб текст і зображення були зосереджені. Ми не хочемо, щоб зображення і текст були центрировані окремо, ми хочемо, щоб зображення і текст були центрировані разом як єдине ціле. Це насправді те, що вже робить UIButton, тому нам просто потрібно відрегулювати інтервал.

CGFloat spacing = 10; // the amount of spacing to appear between image and title
tabBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing);
tabBtn.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0);

Я також перетворив це на категорію для UIButton, тому це буде просто у використанні:

UIButton + Position.h

@interface UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing;

@end

UIButton + Позиція.m

@implementation UIButton(ImageTitleCentering)

-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing {
    self.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0);
}

@end

Тому тепер все, що мені потрібно зробити:

[button centerButtonAndImageWithSpacing:10];

І я отримую те, що мені потрібно щоразу. Більше не возиться з крайовими вставками вручну.

EDIT: Зміна зображень та тексту

У відповідь @Javal у коментарях

Використовуючи цей самий механізм, ми можемо поміняти місцями зображення та текст. Щоб здійснити заміну, просто використовуйте від'ємний інтервал, але також додайте ширину тексту та зображення. Для цього знадобиться знати кадри та вже виконати макет.

[self.view layoutIfNeeded];
CGFloat flippedSpacing = -(desiredSpacing + button.currentImage.size.width + button.titleLabel.frame.size.width);
[button centerButtonAndImageWithSpacing:flippedSpacing];

Звичайно, ви, мабуть, захочете зробити для цього хороший метод, потенційно додавши метод другої категорії, це залишається читачеві як вправу.


Якщо у мене є різні заголовки для звичайних та виділених, як я можу перецентрувати його, коли користувач виділить і не виділить кнопку?
користувач102008

@ user102008 Цілком різні назви? Або просто різних кольорів? Позиціонування не повинно змінюватися, якщо ви використовуєте [UIButton setTitleColor:forState:]або навіть [UIButton setTitle:forState:].
Кекоа

5
Як виправити [button sizeToFit], щоб він правильно розмірів?
RonLugge

@RonLugge Я не впевнений, навіщо вам потрібен sizeToFit, тому я не можу відповісти на ваше запитання. Це добре працює для мене без використання sizeToFit.
Кекоа

Мені потрібен sizeToFit, оскільки кнопки / текст є динамічними, тому мені потрібно розмістити кнопку, щоб відповідати мітці (визначеній користувачем). Проблема полягає в тому, що він не компенсує доданий простір. Я завершив її
перенесення

397

Я трохи запізнююсь на цю вечірку, але думаю, що я можу додати щось корисне.

Відповідь Кекоа чудова, але, як зазначає RonLugge, вона може змусити кнопку більше не поважати sizeToFitабо, що ще важливіше, може змусити кнопку обрізати її вміст, коли він має власний розмір. Yikes!

По-перше, хоча

Коротке пояснення того, як я вірю imageEdgeInsetsі titleEdgeInsetsпрацюю:

У документах дляimageEdgeInsets мають таке сказати, зокрема , йдеться :

Використовуйте цю властивість, щоб змінити розмір та розмістити ефективний прямокутник для малювання кнопки. Ви можете вказати різне значення для кожної з чотирьох вставних верхніх (лівих, нижніх, правих). Позитивне значення зменшує або вставляє цей край - переміщуючи його ближче до центру кнопки. Від'ємне значення розширює або перекреслює цю грань.

Я вважаю, що ця документація була написана, уявляючи, що кнопка не має заголовка, а лише зображення. Це має набагато більше сенсу думати про такий спосіб і поводити себе, як UIEdgeInsetsзазвичай. В основному, рамка зображення (або заголовок, з titleEdgeInsets) переміщується всередину для позитивних вводів і назовні для негативних вставки.

Добре, так що?

Я туди потрапляю! Ось що у вас є за замовчуванням, встановивши зображення та заголовок (рамка кнопки зелена, щоб показати, де вона знаходиться):

Початкове зображення;  немає місця між заголовком та зображенням.

Якщо ви хочете, щоб проміжки між зображенням і заголовком були, не спричиняючи жодного стискання, вам потрібно встановити чотири різних вставки, по два на кожному зображенні та заголовку. Це тому, що ви не хочете змінювати розміри рамок цих елементів, а лише їх положення. Коли ви починаєте мислити таким чином, необхідна зміна відмінної категорії Кекоа стає зрозумілою:

@implementation UIButton(ImageTitleCentering)

- (void)centerButtonAndImageWithSpacing:(CGFloat)spacing {
    CGFloat insetAmount = spacing / 2.0;
    self.imageEdgeInsets = UIEdgeInsetsMake(0, -insetAmount, 0, insetAmount);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, -insetAmount);
}

@end

Але зачекайте , ви кажете, коли я це роблю, я отримую це:

Пробіл хороший, але зображення та назва за межами рамки перегляду.

О так! Я забув, документи про це попередили мене. Кажуть, частково:

Ця властивість використовується лише для розташування зображення під час компонування. Кнопка не використовує цю властивість для визначення intrinsicContentSizeта sizeThatFits:.

Але є властивість, яка може допомогти, і це contentEdgeInsets. Документи для цього говорять частково:

Кнопка використовує цю властивість для визначення intrinsicContentSizeта sizeThatFits:.

Це звучить непогано. Тож давайте ще раз підробимо категорію:

@implementation UIButton(ImageTitleCentering)

- (void)centerButtonAndImageWithSpacing:(CGFloat)spacing {
    CGFloat insetAmount = spacing / 2.0;
    self.imageEdgeInsets = UIEdgeInsetsMake(0, -insetAmount, 0, insetAmount);
    self.titleEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, -insetAmount);
    self.contentEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, insetAmount);
}

@end

А що ви отримуєте?

Розміщення та рамка тепер правильні.

Мені схоже на переможця.


Працюючи в Свіфті і взагалі не хочете думати? Ось остаточна версія розширення у Swift:

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount, bottom: 0, right: insetAmount)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: -insetAmount)
        contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount)
    }
}

25
Яка чудова відповідь! Так, декілька років спізнюються з партією, але це вирішило питання про intrinsicContentSizeте, що вона є невірною, що дуже важливо в ці дні автоматичного розміщення з моменту прийняття оригінальної відповіді.
Есі

2
І якщо ви хотіли однакової відстані між зовнішньою spacingстороною кнопки та зображенням та міткою, то додайте до кожного з чотирьох значень self.contentEdgeInsets, як-от так:self.contentEdgeInsets = UIEdgeInsetsMake(spacing, spacing + insetAmount, spacing, spacing + insetAmount);
Ерік ван дер Неут

1
Чудова відповідь, шкода, що це працює не дуже добре, коли зображення вирівнюється правильно, а довжина тексту може змінюватися.
jlpiedrahita

2
Майже ідеальна відповідь! Відсутнє лише те, що вставки зображення та заголовка повинні бути перевернуті під час роботи інтерфейсу справа наліво.
jeeeyul

як встановити співвідношення зображень на 1 до 1
Єстей Муратов

39

В інтерфейсі Builder. Виберіть UIButton -> Інспектор атрибутів -> Edge = Назва та змініть крайові вставки


38

Також якщо ви хочете зробити щось подібне

введіть тут опис зображення

Тобі потрібно

1.Встановіть горизонтальне та вертикальне вирівнювання для кнопки до

введіть тут опис зображення

  1. Знайдіть усі необхідні значення та встановіть UIImageEdgeInsets

            CGSize buttonSize = button.frame.size;
            NSString *buttonTitle = button.titleLabel.text;
            CGSize titleSize = [buttonTitle sizeWithAttributes:@{ NSFontAttributeName : [UIFont camFontZonaProBoldWithSize:12.f] }];
            UIImage *buttonImage = button.imageView.image;
            CGSize buttonImageSize = buttonImage.size;
    
            CGFloat offsetBetweenImageAndText = 10; //vertical space between image and text
    
            [button setImageEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 - offsetBetweenImageAndText,
                                                        (buttonSize.width - buttonImageSize.width) / 2,
                                                        0,0)];                
            [button setTitleEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 + buttonImageSize.height + offsetBetweenImageAndText,
                                                        titleSize.width + [button imageEdgeInsets].left > buttonSize.width ? -buttonImage.size.width  +  (buttonSize.width - titleSize.width) / 2 : (buttonSize.width - titleSize.width) / 2 - buttonImage.size.width,
                                                        0,0)];

Це впорядкує вашу назву та зображення на кнопці.

Також зверніть увагу, оновіть це на кожній ретрансляції


Швидкий

import UIKit

extension UIButton {
    // MARK: - UIButton+Aligment

    func alignContentVerticallyByCenter(offset:CGFloat = 10) {
        let buttonSize = frame.size

        if let titleLabel = titleLabel,
            let imageView = imageView {

            if let buttonTitle = titleLabel.text,
                let image = imageView.image {
                let titleString:NSString = NSString(string: buttonTitle)
                let titleSize = titleString.sizeWithAttributes([
                    NSFontAttributeName : titleLabel.font
                    ])
                let buttonImageSize = image.size

                let topImageOffset = (buttonSize.height - (titleSize.height + buttonImageSize.height + offset)) / 2
                let leftImageOffset = (buttonSize.width - buttonImageSize.width) / 2
                imageEdgeInsets = UIEdgeInsetsMake(topImageOffset,
                                                   leftImageOffset,
                                                   0,0)

                let titleTopOffset = topImageOffset + offset + buttonImageSize.height
                let leftTitleOffset = (buttonSize.width - titleSize.width) / 2 - image.size.width

                titleEdgeInsets = UIEdgeInsetsMake(titleTopOffset,
                                                   leftTitleOffset,
                                                   0,0)
            }
        }
    }
}

29

Ви можете уникнути багато проблем, скориставшись цим -

myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;   
myButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;

Це автоматично вирівняє весь ваш вміст ліворуч (або де завгодно)

Швидкий 3:

myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left;   
myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center;

myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center; //
Друк

25

У Xcode 8.0 ви можете просто зробити це, змінившиinsets інспектора розміру.

Виберіть UIButton -> Інспектор атрибутів -> перейдіть до інспектора розміру та змініть вставки, зображення та заголовки.

введіть тут опис зображення

І якщо ви хочете змінити зображення з правого боку, ви можете просто змінити семантичну властивість на Force Right-to-leftінспектор атрибутів.

введіть тут опис зображення


у моєму випадку зображення кнопки ніколи не переміщується до правої частини тексту з xcode 10? Ви можете допомогти?
Сатіш Мавані

Привіт Сатиш, це також чудово працює з xcode 10. Сподіваюся, ви встановлюєте зображення не фонове зображення, а також можете змінювати зображення комах, використовуючи інспектор розміру.
Сахіл

18

Я теж трохи запізнююся на цю вечірку, але думаю, що я можу додати щось корисне: o).

Я створив UIButtonпідклас, мета якого - вибрати можливість розташування зображення кнопки - вертикально чи горизонтально.

Це означає, що ви можете робити такі кнопки: кнопки різного виду

Ось деталі про те, як створити ці кнопки з моїм класом:

func makeButton (imageVerticalAlignment:LayoutableButton.VerticalAlignment, imageHorizontalAlignment:LayoutableButton.HorizontalAlignment, title:String) -> LayoutableButton {
    let button = LayoutableButton ()

    button.imageVerticalAlignment = imageVerticalAlignment
    button.imageHorizontalAlignment = imageHorizontalAlignment

    button.setTitle(title, for: .normal)

    // add image, border, ...

    return button
}

let button1 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .left, title: "button1")
let button2 = makeButton(imageVerticalAlignment: .center, imageHorizontalAlignment: .right, title: "button2")
let button3 = makeButton(imageVerticalAlignment: .top, imageHorizontalAlignment: .center, title: "button3")
let button4 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button4")
let button5 = makeButton(imageVerticalAlignment: .bottom, imageHorizontalAlignment: .center, title: "button5")
button5.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)

Для цього я додав 2 атрибути: imageVerticalAlignmentі imageHorizontalAlignment. Звичайно, якщо на вашій кнопці є лише зображення або назва ... взагалі не використовуйте цей клас!

Я також додав атрибут на ім'я imageToTitleSpacing який дозволяє регулювати простір між заголовком та зображенням.

Цей клас намагається бути сумісним, якщо ви хочете використовувати imageEdgeInsets, titleEdgeInsetsі contentEdgeInsetsбезпосередньо, або в поєднанні з новими атрибутами компонування.

Як пояснює @ravron, я намагаюся зробити правильний край вмісту кнопки (як ви бачите з червоними рамками).

Ви також можете використовувати його в Interface Builder:

  1. Створіть UIButton
  2. Змініть клас клавіш
  3. Відрегулюйте атрибути, розташовані за допомогою "центру", "вгорі", "знизу", "зліва" або "праворуч" кнопкові атрибути

Ось код ( суть ):

@IBDesignable
class LayoutableButton: UIButton {

    enum VerticalAlignment : String {
        case center, top, bottom, unset
    }


    enum HorizontalAlignment : String {
        case center, left, right, unset
    }


    @IBInspectable
    var imageToTitleSpacing: CGFloat = 8.0 {
        didSet {
            setNeedsLayout()
        }
    }


    var imageVerticalAlignment: VerticalAlignment = .unset {
        didSet {
            setNeedsLayout()
        }
    }

    var imageHorizontalAlignment: HorizontalAlignment = .unset {
        didSet {
            setNeedsLayout()
        }
    }

    @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageVerticalAlignment' instead.")
    @IBInspectable
    var imageVerticalAlignmentName: String {
        get {
            return imageVerticalAlignment.rawValue
        }
        set {
            if let value = VerticalAlignment(rawValue: newValue) {
                imageVerticalAlignment = value
            } else {
                imageVerticalAlignment = .unset
            }
        }
    }

    @available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageHorizontalAlignment' instead.")
    @IBInspectable
    var imageHorizontalAlignmentName: String {
        get {
            return imageHorizontalAlignment.rawValue
        }
        set {
            if let value = HorizontalAlignment(rawValue: newValue) {
                imageHorizontalAlignment = value
            } else {
                imageHorizontalAlignment = .unset
            }
        }
    }

    var extraContentEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero

    override var contentEdgeInsets: UIEdgeInsets {
        get {
            return super.contentEdgeInsets
        }
        set {
            super.contentEdgeInsets = newValue
            self.extraContentEdgeInsets = newValue
        }
    }

    var extraImageEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero

    override var imageEdgeInsets: UIEdgeInsets {
        get {
            return super.imageEdgeInsets
        }
        set {
            super.imageEdgeInsets = newValue
            self.extraImageEdgeInsets = newValue
        }
    }

    var extraTitleEdgeInsets:UIEdgeInsets = UIEdgeInsets.zero

    override var titleEdgeInsets: UIEdgeInsets {
        get {
            return super.titleEdgeInsets
        }
        set {
            super.titleEdgeInsets = newValue
            self.extraTitleEdgeInsets = newValue
        }
    }

    //Needed to avoid IB crash during autolayout
    override init(frame: CGRect) {
        super.init(frame: frame)
    }


    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.imageEdgeInsets = super.imageEdgeInsets
        self.titleEdgeInsets = super.titleEdgeInsets
        self.contentEdgeInsets = super.contentEdgeInsets
    }

    override func layoutSubviews() {
        if let imageSize = self.imageView?.image?.size,
            let font = self.titleLabel?.font,
            let textSize = self.titleLabel?.attributedText?.size() ?? self.titleLabel?.text?.size(attributes: [NSFontAttributeName: font]) {

            var _imageEdgeInsets = UIEdgeInsets.zero
            var _titleEdgeInsets = UIEdgeInsets.zero
            var _contentEdgeInsets = UIEdgeInsets.zero

            let halfImageToTitleSpacing = imageToTitleSpacing / 2.0

            switch imageVerticalAlignment {
            case .bottom:
                _imageEdgeInsets.top = (textSize.height + imageToTitleSpacing) / 2.0
                _imageEdgeInsets.bottom = (-textSize.height - imageToTitleSpacing) / 2.0
                _titleEdgeInsets.top = (-imageSize.height - imageToTitleSpacing) / 2.0
                _titleEdgeInsets.bottom = (imageSize.height + imageToTitleSpacing) / 2.0
                _contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                _contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                //only works with contentVerticalAlignment = .center
                contentVerticalAlignment = .center
            case .top:
                _imageEdgeInsets.top = (-textSize.height - imageToTitleSpacing) / 2.0
                _imageEdgeInsets.bottom = (textSize.height + imageToTitleSpacing) / 2.0
                _titleEdgeInsets.top = (imageSize.height + imageToTitleSpacing) / 2.0
                _titleEdgeInsets.bottom = (-imageSize.height - imageToTitleSpacing) / 2.0
                _contentEdgeInsets.top = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                _contentEdgeInsets.bottom = (min (imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0
                //only works with contentVerticalAlignment = .center
                contentVerticalAlignment = .center
            case .center:
                //only works with contentVerticalAlignment = .center
                contentVerticalAlignment = .center
                break
            case .unset:
                break
            }

            switch imageHorizontalAlignment {
            case .left:
                _imageEdgeInsets.left = -halfImageToTitleSpacing
                _imageEdgeInsets.right = halfImageToTitleSpacing
                _titleEdgeInsets.left = halfImageToTitleSpacing
                _titleEdgeInsets.right = -halfImageToTitleSpacing
                _contentEdgeInsets.left = halfImageToTitleSpacing
                _contentEdgeInsets.right = halfImageToTitleSpacing
            case .right:
                _imageEdgeInsets.left = textSize.width + halfImageToTitleSpacing
                _imageEdgeInsets.right = -textSize.width - halfImageToTitleSpacing
                _titleEdgeInsets.left = -imageSize.width - halfImageToTitleSpacing
                _titleEdgeInsets.right = imageSize.width + halfImageToTitleSpacing
                _contentEdgeInsets.left = halfImageToTitleSpacing
                _contentEdgeInsets.right = halfImageToTitleSpacing
            case .center:
                _imageEdgeInsets.left = textSize.width / 2.0
                _imageEdgeInsets.right = -textSize.width / 2.0
                _titleEdgeInsets.left = -imageSize.width / 2.0
                _titleEdgeInsets.right = imageSize.width / 2.0
                _contentEdgeInsets.left = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0
                _contentEdgeInsets.right = -((imageSize.width + textSize.width) - max (imageSize.width, textSize.width)) / 2.0
            case .unset:
                break
            }

            _contentEdgeInsets.top += extraContentEdgeInsets.top
            _contentEdgeInsets.bottom += extraContentEdgeInsets.bottom
            _contentEdgeInsets.left += extraContentEdgeInsets.left
            _contentEdgeInsets.right += extraContentEdgeInsets.right

            _imageEdgeInsets.top += extraImageEdgeInsets.top
            _imageEdgeInsets.bottom += extraImageEdgeInsets.bottom
            _imageEdgeInsets.left += extraImageEdgeInsets.left
            _imageEdgeInsets.right += extraImageEdgeInsets.right

            _titleEdgeInsets.top += extraTitleEdgeInsets.top
            _titleEdgeInsets.bottom += extraTitleEdgeInsets.bottom
            _titleEdgeInsets.left += extraTitleEdgeInsets.left
            _titleEdgeInsets.right += extraTitleEdgeInsets.right

            super.imageEdgeInsets = _imageEdgeInsets
            super.titleEdgeInsets = _titleEdgeInsets
            super.contentEdgeInsets = _contentEdgeInsets

        } else {
            super.imageEdgeInsets = extraImageEdgeInsets
            super.titleEdgeInsets = extraTitleEdgeInsets
            super.contentEdgeInsets = extraContentEdgeInsets
        }

        super.layoutSubviews()
    }
}

1
Я виправляю деякі речі, щоб не порушувати ІБ error: IB Designables: Failed to update auto layout status: The agent crashed, gist.github.com/nebiros/ecf69ff9cb90568edde071386c6c4ddb
nebiros

@nebiros чи можете ви пояснити, що не так, і як це виправити?
gbitaudeau

@gbitaudeau, коли я копіюю та вставляю скрипт, я отримав цю помилку, error: IB Designables: Failed to update auto layout status: The agent crashedтому що init(frame: CGRect)не було відмінено, я також додаю @availableанотацію…, ви можете, diff -Naurякщо хочете, ;-)
nebiros

9

Невелике доповнення до відповіді Райлі Аврона на зміну місцевості облікового запису:

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        let writingDirection = UIApplication.sharedApplication().userInterfaceLayoutDirection
        let factor: CGFloat = writingDirection == .LeftToRight ? 1 : -1

        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount*factor, bottom: 0, right: insetAmount*factor)
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount*factor, bottom: 0, right: -insetAmount*factor)
        self.contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount)
    }
}

6

Швидкий 4.x

extension UIButton {
    func centerTextAndImage(spacing: CGFloat) {
        let insetAmount = spacing / 2
        let writingDirection = UIApplication.shared.userInterfaceLayoutDirection
        let factor: CGFloat = writingDirection == .leftToRight ? 1 : -1

        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount*factor, bottom: 0, right: insetAmount*factor)
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount*factor, bottom: 0, right: -insetAmount*factor)
        self.contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount)
    }
}

Використання :

button.centerTextAndImage(spacing: 10.0)

Як можна використовувати розмір зображення костіома?
midhun p

2

Я пишу код нижче. Він добре працює у версії продукту. Supprot Swift 4.2 +

extension UIButton{
 enum ImageTitleRelativeLocation {
    case imageUpTitleDown
    case imageDownTitleUp
    case imageLeftTitleRight
    case imageRightTitleLeft
}
 func centerContentRelativeLocation(_ relativeLocation: 
                                      ImageTitleRelativeLocation,
                                   spacing: CGFloat = 0) {
    assert(contentVerticalAlignment == .center,
           "only works with contentVerticalAlignment = .center !!!")

    guard (title(for: .normal) != nil) || (attributedTitle(for: .normal) != nil) else {
        assert(false, "TITLE IS NIL! SET TITTLE FIRST!")
        return
    }

    guard let imageSize = self.currentImage?.size else {
        assert(false, "IMGAGE IS NIL! SET IMAGE FIRST!!!")
        return
    }
    guard let titleSize = titleLabel?
        .systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) else {
            assert(false, "TITLELABEL IS NIL!")
            return
    }

    let horizontalResistent: CGFloat
    // extend contenArea in case of title is shrink
    if frame.width < titleSize.width + imageSize.width {
        horizontalResistent = titleSize.width + imageSize.width - frame.width
        print("horizontalResistent", horizontalResistent)
    } else {
        horizontalResistent = 0
    }

    var adjustImageEdgeInsets: UIEdgeInsets = .zero
    var adjustTitleEdgeInsets: UIEdgeInsets = .zero
    var adjustContentEdgeInsets: UIEdgeInsets = .zero

    let verticalImageAbsOffset = abs((titleSize.height + spacing) / 2)
    let verticalTitleAbsOffset = abs((imageSize.height + spacing) / 2)

    switch relativeLocation {
    case .imageUpTitleDown:

        adjustImageEdgeInsets.top = -verticalImageAbsOffset
        adjustImageEdgeInsets.bottom = verticalImageAbsOffset
        adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
        adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2

        adjustTitleEdgeInsets.top = verticalTitleAbsOffset
        adjustTitleEdgeInsets.bottom = -verticalTitleAbsOffset
        adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
        adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2

        adjustContentEdgeInsets.top = spacing
        adjustContentEdgeInsets.bottom = spacing
        adjustContentEdgeInsets.left = -horizontalResistent
        adjustContentEdgeInsets.right = -horizontalResistent
    case .imageDownTitleUp:
        adjustImageEdgeInsets.top = verticalImageAbsOffset
        adjustImageEdgeInsets.bottom = -verticalImageAbsOffset
        adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
        adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2

        adjustTitleEdgeInsets.top = -verticalTitleAbsOffset
        adjustTitleEdgeInsets.bottom = verticalTitleAbsOffset
        adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
        adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2

        adjustContentEdgeInsets.top = spacing
        adjustContentEdgeInsets.bottom = spacing
        adjustContentEdgeInsets.left = -horizontalResistent
        adjustContentEdgeInsets.right = -horizontalResistent
    case .imageLeftTitleRight:
        adjustImageEdgeInsets.left = -spacing / 2
        adjustImageEdgeInsets.right = spacing / 2

        adjustTitleEdgeInsets.left = spacing / 2
        adjustTitleEdgeInsets.right = -spacing / 2

        adjustContentEdgeInsets.left = spacing
        adjustContentEdgeInsets.right = spacing
    case .imageRightTitleLeft:
        adjustImageEdgeInsets.left = titleSize.width + spacing / 2
        adjustImageEdgeInsets.right = -titleSize.width - spacing / 2

        adjustTitleEdgeInsets.left = -imageSize.width - spacing / 2
        adjustTitleEdgeInsets.right = imageSize.width + spacing / 2

        adjustContentEdgeInsets.left = spacing
        adjustContentEdgeInsets.right = spacing
    }

    imageEdgeInsets = adjustImageEdgeInsets
    titleEdgeInsets = adjustTitleEdgeInsets
    contentEdgeInsets = adjustContentEdgeInsets

    setNeedsLayout()
}
}

1

Ось простий приклад використання зображення imageEdgeInsets Це дозволить зробити кнопку 30x30 із вражаючою площею на 10 пікселів більше (50x50)

    var expandHittableAreaAmt : CGFloat = 10
    var buttonWidth : CGFloat = 30
    var button = UIButton.buttonWithType(UIButtonType.Custom) as UIButton
    button.frame = CGRectMake(0, 0, buttonWidth+expandHittableAreaAmt, buttonWidth+expandHittableAreaAmt)
    button.imageEdgeInsets = UIEdgeInsetsMake(expandHittableAreaAmt, expandHittableAreaAmt, expandHittableAreaAmt, expandHittableAreaAmt)
    button.setImage(UIImage(named: "buttonImage"), forState: .Normal)
    button.addTarget(self, action: "didTouchButton:", forControlEvents:.TouchUpInside)

0

Елегантний спосіб у Swift 3 та краще зрозуміти:

override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
    let leftMargin:CGFloat = 40
    let imgWidth:CGFloat = 24
    let imgHeight:CGFloat = 24
    return CGRect(x: leftMargin, y: (contentRect.size.height-imgHeight) * 0.5, width: imgWidth, height: imgHeight)
}

override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
    let leftMargin:CGFloat = 80
    let rightMargin:CGFloat = 80
    return CGRect(x: leftMargin, y: 0, width: contentRect.size.width-leftMargin-rightMargin, height: contentRect.size.height)
}
override func backgroundRect(forBounds bounds: CGRect) -> CGRect {
    let leftMargin:CGFloat = 10
    let rightMargin:CGFloat = 10
    let topMargin:CGFloat = 10
    let bottomMargin:CGFloat = 10
    return CGRect(x: leftMargin, y: topMargin, width: bounds.size.width-leftMargin-rightMargin, height: bounds.size.height-topMargin-bottomMargin)
}
override func contentRect(forBounds bounds: CGRect) -> CGRect {
    let leftMargin:CGFloat = 5
    let rightMargin:CGFloat = 5
    let topMargin:CGFloat = 5
    let bottomMargin:CGFloat = 5
    return CGRect(x: leftMargin, y: topMargin, width: bounds.size.width-leftMargin-rightMargin, height: bounds.size.height-topMargin-bottomMargin)
}

-1

Швидкою версією рішення 4.2 було б таке:

let spacing: CGFloat = 10 // the amount of spacing to appear between image and title
self.button?.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: spacing)
self.button?.titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: 0)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.