Как поместить изображение в правую часть текста в UIButton?

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

Ответ 1

Несмотря на то, что некоторые из предложенных ответов очень креативны и чрезвычайно умны, самое простое решение заключается в следующем:

button.semanticContentAttribute = UIApplication.shared
    .userInterfaceLayoutDirection == .rightToLeft ? .forceLeftToRight : .forceRightToLeft

Так просто. В качестве бонуса, изображение будет слева на правой стороне слева.

РЕДАКТИРОВАТЬ: как вопрос был задан несколько раз, это iOS 9 +.

Ответ 2

Самое простое решение:

iOS 10 и выше, Swift:

button.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.titleLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)

До iOS 10, Swift/Obj-C:

button.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.titleLabel.transform = CGAffineTransformMakeScale(-1.0, 1.0);
button.imageView.transform = CGAffineTransformMakeScale(-1.0, 1.0);

Ответ 3

ОБНОВЛЕНО ДЛЯ XCODE 9 (через Интерфейсный Разработчик)

Есть более простой способ от Interface Builder. Выберите UIButton и выберите эту опцию в View Utilities:

enter image description here

Это! Красиво и просто!

ДОПОЛНИТЕЛЬНО - 2-й шаг:

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

enter image description here

Надеюсь, это поможет!

Ответ 4

Подклассификация UIButton совершенно не нужна. Вместо этого вы можете просто установить высокое значение левой вставки для вставки изображения и небольшую правую вставку для названия. Что-то вроде этого:

button.imageEdgeInsets = UIEdgeInsetsMake(0., button.frame.size.width - (image.size.width + 15.), 0., 0.);
button.titleEdgeInsets = UIEdgeInsetsMake(0., 0., 0., image.size.width);

Ответ 5

Я даю Inspire48 кредит для этого. Основываясь на его предположении и рассматривая этот другой вопрос, я придумал это. Подкласс UIButton и переопределить эти методы.

@implementation UIButtonSubclass

- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
    CGRect frame = [super imageRectForContentRect:contentRect];
    frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(frame) -  self.imageEdgeInsets.right + self.imageEdgeInsets.left;
    return frame;
}

- (CGRect)titleRectForContentRect:(CGRect)contentRect
{
    CGRect frame = [super titleRectForContentRect:contentRect];
    frame.origin.x = CGRectGetMinX(frame) - CGRectGetWidth([self imageRectForContentRect:contentRect]);
    return frame;
}

@end

Ответ 6

Просто обновите вставки, когда заголовок будет изменен. Вы должны компенсировать вставку с равной и противоположной вставкой с другой стороны.

[thebutton setTitle:title forState:UIControlStateNormal];
thebutton.titleEdgeInsets = UIEdgeInsetsMake(0, -thebutton.imageView.frame.size.width, 0, thebutton.imageView.frame.size.width);
thebutton.imageEdgeInsets = UIEdgeInsetsMake(0, thebutton.titleLabel.frame.size.width, 0, -thebutton.titleLabel.frame.size.width);

Ответ 7

Все эти ответы, начиная с января 2016 года, не нужны. В Interface Builder установите View Semantic в значение Force Right-to-Left, или если вы предпочитаете программный путь, semanticContentAttribute = .forceRightToLeft Это приведет к появлению изображения справа от вашего текста.

Ответ 8

В построителе интерфейсов вы можете настроить опции Edge Insets для UIButton, отдельно каждую из трех частей: содержимое, изображение, название

enter image description hereenter image description here

Xcode 8:

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

Ответ 9

Обновление: Swift 3

class ButtonIconRight: UIButton {
    override func imageRect(forContentRect contentRect:CGRect) -> CGRect {
        var imageFrame = super.imageRect(forContentRect: contentRect)
        imageFrame.origin.x = super.titleRect(forContentRect: contentRect).maxX - imageFrame.width
        return imageFrame
    }

    override func titleRect(forContentRect contentRect:CGRect) -> CGRect {
        var titleFrame = super.titleRect(forContentRect: contentRect)
        if (self.currentImage != nil) {
            titleFrame.origin.x = super.imageRect(forContentRect: contentRect).minX
        }
        return titleFrame
    }
}

Оригинальный ответ для Swift 2:

Решение, которое обрабатывает все горизонтальные выравнивания, с примером реализации Swift. Просто переведите на Objective-C при необходимости.

class ButtonIconRight: UIButton {
    override func imageRectForContentRect(contentRect:CGRect) -> CGRect {
        var imageFrame = super.imageRectForContentRect(contentRect)
        imageFrame.origin.x = CGRectGetMaxX(super.titleRectForContentRect(contentRect)) - CGRectGetWidth(imageFrame)
        return imageFrame
    }

    override func titleRectForContentRect(contentRect:CGRect) -> CGRect {
        var titleFrame = super.titleRectForContentRect(contentRect)
        if (self.currentImage != nil) {
            titleFrame.origin.x = CGRectGetMinX(super.imageRectForContentRect(contentRect))
        }
        return titleFrame
    }
}

Также стоит отметить, что он отлично обрабатывает вставки изображений и заголовков.

Вдохновленный от ответа на jasongregori;)

Ответ 10

Вот решение для UIButton с выровненным по центру контентом. Этот код выравнивает изображение и позволяет использовать imageEdgeInsets и titleEdgeInsets для драгоценного позиционирования.

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

Подкласс UIButton с вашим специальным классом и добавьте:

- (CGRect)imageRectForContentRect:(CGRect)contentRect {
    CGRect frame = [super imageRectForContentRect:contentRect];
    CGFloat imageWidth = frame.size.width;
    CGRect titleRect = CGRectZero;
    titleRect.size = [[self titleForState:self.state] sizeWithAttributes:@{NSFontAttributeName: self.titleLabel.font}];
    titleRect.origin.x = (self.frame.size.width - (titleRect.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    frame.origin.x = titleRect.origin.x + titleRect.size.width - self.imageEdgeInsets.right + self.imageEdgeInsets.left;
    return frame;
}

- (CGRect)titleRectForContentRect:(CGRect)contentRect {
    CGFloat imageWidth = [self imageForState:self.state].size.width;
    CGRect frame = [super titleRectForContentRect:contentRect];
    frame.origin.x = (self.frame.size.width - (frame.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    return frame;
}

Ответ 11

Если это нужно сделать в UIBarButtonItem, следует использовать дополнительную упаковку в представлении Это будет работать

let view = UIView()
let button = UIButton()
button.setTitle("Skip", for: .normal)
button.setImage(#imageLiteral(resourceName:"forward_button"), for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.sizeToFit()
view.addSubview(button)
view.frame = button.bounds
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: view)

Это не сработает

let button = UIButton()
button.setTitle("Skip", for: .normal)
button.setImage(#imageLiteral(resourceName:"forward_button"), for: .normal)
button.semanticContentAttribute = .forceRightToLeft
button.sizeToFit()
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)

Ответ 12

Поскольку решение для преобразования не работает в iOS 11, я решил написать новый подход.

Регулировка кнопок semanticContentAttribute дает нам изображение справа вправо без необходимости ретрансляции, если текст изменяется. Из-за этого это идеальное решение. Однако мне все еще нужна поддержка RTL. Тот факт, что приложение не может изменить направление компоновки в одном сеансе, легко решает эту проблему.

С учетом сказанного, это довольно просто.

extension UIButton {
    func alignImageRight() {
        if UIApplication.shared.userInterfaceLayoutDirection == .leftToRight {
            semanticContentAttribute = .forceRightToLeft
        }
        else {
            semanticContentAttribute = .forceLeftToRight
        }
    }
}

Ответ 13

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

    CGFloat spacing          = 3;
    CGFloat insetAmount      = 0.5 * spacing;

    // First set overall size of the button:
    button.contentEdgeInsets = UIEdgeInsetsMake(0, insetAmount, 0, insetAmount);
    [button sizeToFit];

    // Then adjust title and image insets so image is flipped to the right and there is spacing between title and image:
    button.titleEdgeInsets   = UIEdgeInsetsMake(0, -button.imageView.frame.size.width - insetAmount, 0,  button.imageView.frame.size.width  + insetAmount);
    button.imageEdgeInsets   = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width + insetAmount, 0, -button.titleLabel.frame.size.width - insetAmount);

Спасибо Piotr за ваше решение!

Эрик

Ответ 14

Свифт -Extend UiButton и поместите эти строки

    if let imageWidth = self.imageView?.frame.width {
        self.titleEdgeInsets = UIEdgeInsetsMake(0, -imageWidth, 0, imageWidth);
    }

    if let titleWidth = self.titleLabel?.frame.width {
        let spacing = titleWidth + 20
        self.imageEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, -spacing);
    }

Ответ 15

Взял @Piotr ответ и превратил его в расширение Swift. Обязательно установите изображение и заголовок, прежде чем вызывать это, чтобы кнопка правильно отображалась.

extension UIButton {

/// Makes the ``imageView`` appear just to the right of the ``titleLabel``.
func alignImageRight() {
    if let titleLabel = self.titleLabel, imageView = self.imageView {
        // Force the label and image to resize.
        titleLabel.sizeToFit()
        imageView.sizeToFit()
        imageView.contentMode = .ScaleAspectFit

        // Set the insets so that the title appears to the left and the image appears to the right. 
        // Make the image appear slightly off the top/bottom edges of the button.
        self.titleEdgeInsets = UIEdgeInsets(top: 0, left: -1 * imageView.frame.size.width,
            bottom: 0, right: imageView.frame.size.width)
        self.imageEdgeInsets = UIEdgeInsets(top: 4, left: titleLabel.frame.size.width,
            bottom: 4, right: -1 * titleLabel.frame.size.width)
    }
}

}

Ответ 16

Быстрый вариант, который делает то, что вы хотите, не играя с любыми вставками:

class RightImageButton: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        if let  textSize = titleLabel?.intrinsicContentSize(),
                imageSize = imageView?.intrinsicContentSize() {
            let wholeWidth = textSize.width + K.textImageGap + imageSize.width
            titleLabel?.frame = CGRect(
                x: round(bounds.width/2 - wholeWidth/2),
                y: 0,
                width: ceil(textSize.width),
                height: bounds.height)
            imageView?.frame = CGRect(
                x: round(bounds.width/2 + wholeWidth/2 - imageSize.width),
                y: RoundRetina(bounds.height/2 - imageSize.height/2),
                width: imageSize.width,
                height: imageSize.height)
        }
    }

    struct K {
        static let textImageGap: CGFloat = 5
    }

}

Ответ 18

Решения, упомянутые здесь, перестали работать, как только я включил Автоматический макет. Я должен был придумать свой собственный:

Подкласс UIButton и переопределить метод layoutSubviews:

//
//  MIThemeButtonImageAtRight.m
//  Created by Lukasz Margielewski on 7/9/13.
//

#import "MIThemeButtonImageAtRight.h"

static CGRect CGRectByApplyingUIEdgeInsets(CGRect frame, UIEdgeInsets insets);

@implementation MIThemeButtonImageAtRight

- (void)layoutSubviews
{
    [super layoutSubviews];

    CGRect contentFrame = CGRectByApplyingUIEdgeInsets(self.bounds, self.contentEdgeInsets);

    CGRect frameIcon = self.imageView.frame;
    CGRect frameText = self.titleLabel.frame;

    frameText.origin.x = CGRectGetMinX(contentFrame) + self.titleEdgeInsets.left;
    frameIcon.origin.x = CGRectGetMaxX(contentFrame) - CGRectGetWidth(frameIcon);

    self.imageView.frame = frameIcon;
    self.titleLabel.frame = frameText;
}

@end

static CGRect CGRectByApplyingUIEdgeInsets(CGRect frame, UIEdgeInsets insets){

    CGRect f = frame;

    f.origin.x += insets.left;
    f.size.width -= (insets.left + insets.right);
    f.origin.y += (insets.top);
    f.size.height -= (insets.top + insets.bottom);

    return f;

}

Результат:

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

Ответ 19

Путь расширения

Используя расширение, чтобы установить изображение на правой стороне с пользовательским смещением

   extension UIButton {
    func addRightImage(image: UIImage, offset: CGFloat) {
        self.setImage(image, for: .normal)
        self.imageView?.translatesAutoresizingMaskIntoConstraints = false
        self.imageView?.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0.0).isActive = true
        self.imageView?.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -offset).isActive = true
    }
}

Ответ 20

Swift 3:

open override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
    var frame = super.imageRect(forContentRect: contentRect)
    let  imageWidth = frame.size.width
    var titleRect = CGRect.zero
    titleRect.size = self.title(for: self.state)!.size(attributes: [NSFontAttributeName: self.titleLabel!.font])
    titleRect.origin.x = (self.frame.size.width - (titleRect.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    frame.origin.x = titleRect.origin.x + titleRect.size.width - self.imageEdgeInsets.right + self.imageEdgeInsets.left;
    return frame
}

open override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
    var frame = super.titleRect(forContentRect: contentRect)
    if let imageWidth = self.image(for: self.state)?.size.width {
        frame.origin.x = (self.frame.size.width - (frame.size.width + imageWidth)) / 2.0 + self.titleEdgeInsets.left - self.titleEdgeInsets.right;
    }
    return frame
}

Ответ 21

swift 3.0 Миграция  решение, данное jasongregori

class ButtonIconRight: UIButton {
        override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
            var imageFrame = super.imageRect(forContentRect: contentRect)
           imageFrame.origin.x = super.titleRect(forContentRect: contentRect).maxX - imageFrame.width
        return imageFrame
        }

        override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
            var titleFrame = super.titleRect(forContentRect: contentRect)
            if (self.currentImage != nil) {
                titleFrame.origin.x = super.imageRect(forContentRect: contentRect).minX
            }
            return titleFrame
        }

Ответ 22

Как насчет ограничений? В отличие от semanticContentAttribute, они не меняют семантики. Что-то вроде этого возможно:

 button.rightAnchorconstraint(equalTo: button.rightAnchor).isActive = true

или Objective-C:

[button.imageView.rightAnchor constraintEqualToAnchor:button.rightAnchor].isActive = YES;

Предостережения: Untested, iOS 9 +

Ответ 23

Попробовав несколько решений по всему интернету, я не достиг точного требования. Так что я закончил писать собственный код утилиты. Публикация, чтобы помочь кому-то в будущем. Проверено на Swift 4.2

// This function should be called in/after viewDidAppear to let view render
    func addArrowImageToButton(button: UIButton, arrowImage:UIImage = #imageLiteral(resourceName: "my_image_name") ) {
        let btnSize:CGFloat = 32
        let imageView = UIImageView(image: arrowImage)
        let btnFrame = button.frame
        imageView.frame = CGRect(x: btnFrame.width-btnSize-8, y: btnFrame.height/2 - btnSize/2, width: btnSize, height: btnSize)
        button.addSubview(imageView)
        //Imageview on Top of View
        button.bringSubviewToFront(imageView)
    }

Ответ 24

Благодаря Виталию Гоженко

Я просто хочу добавить, что вы можете добавить IB_DESIGNABLE перед вашей кнопкой @interface и установить свой класс кнопок в storyborad. Затем вы можете смотреть его макет в режиме реального времени без запуска приложения только на стадии построения интерфейса.

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

Ответ 25

Чтобы выровнять изображение по правому краю в UIButton, попробуйте код ниже

btn.contentHorizontalAlignment = .right

Ответ 26

Правильный ответ в Swift

import UIKit

extension UIButton {

     func imageRectForContentRect(contentRect:CGRect) -> CGRect {
        var frame = self.imageRectForContentRect(contentRect)
        frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(frame) - self.imageEdgeInsets.right + self.imageEdgeInsets.left

        return frame
    }

    func titleRectForContentRect(contentRect:CGRect) -> CGRect {
        var frame = self.titleRectForContentRect(contentRect)
        frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(self.imageRectForContentRect(contentRect))
        return frame
    }
}