iPhone UIButton - положение изображения


116

У меня есть UIButtonтекст «Изучить приложение» и UIImage(>) Interface Builderон выглядит так:

[ (>) Explore the app ]

Но мне нужно поместить это UIImageПОСЛЕ текста:

[ Explore the app (>) ]

Как я могу переместить UIImageвправо?


Вы также можете использовать Interface Builder. Посмотрите мой ответ здесь: stackoverflow.com/questions/2765024/…
n8tr

1
Просто используйте этот подкласс с несколькими строками кода: github.com/k06a/RTLButton
k06a

Ответы:


161

Мое решение довольно простое

[button sizeToFit];
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width, 0, button.imageView.frame.size.width);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, -button.titleLabel.frame.size.width);

4
Это прекрасно работает! Трудно понять концепцию вставки края. Есть идеи, почему нам нужно установить и левую, и правую вставку? Теоретически, если бы я сдвинул заголовок влево, а изображение вправо, этого было бы достаточно. Почему мне нужно устанавливать и левую, и правую?
PokerIncome.com

3
ЛУЧШЕЕ РЕШЕНИЕ, которое я нашел
Бимава

2
Этот ответ работает отлично. Принятый ответ правильный и указывает на правильные документы API, но это решение для копирования и вставки, которое выполняет то, что запросил OP.
mclaughlinj

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

Спасибо, что показали мне, что вы должны применять одинаковую ширину к левой и правой сторонам, я не на 100% понимаю, как это работает (потому что иногда это влияет на размер, а также на происхождение), но я смог решить похожие проблемы используя этот метод.
Тоби

128

В iOS 9 кажется, что простой способ добиться этого - форсировать семантику представления.

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

Или программно, используя:

button.semanticContentAttribute = .ForceRightToLeft

4
Как бы мне ни понравился ваш ответ (+1), я не хочу говорить, что это может быть не «правильный» способ сделать это, но это один из самых простых.
farzadshbfn

@farzadshbfn ты прав. Я изменил это слово на «простой», кажется более последовательным.
Alvivi

@farzadshbfn Почему это не «правильный» способ?
Самман Бикрам Тапа

3
@SammanBikramThapa ИМХО правильным способом было бы создать подкласс UIButton и переопределить layoutSubviews и уважать semanticContentAttributeв нашей логике макета, вместо того, чтобы изменять semanticContentAttributeсебя. (изменение семантического подхода, не будет работать с интернационализацией)
farzadshbfn

@farzadshbfn совершенно прав. Независимо от того, что этот ответ набирает наибольшее количество очков, он неверен - он может нарушить UX для интерфейса справа налево.
Павел Якименко

86

Установите imageEdgeInsetи, titleEdgeInsetчтобы перемещать компоненты внутри изображения. Вы также можете создать кнопку, используя полноразмерную графику, и использовать ее в качестве фонового изображения для кнопки (затем используйте titleEdgeInsetsдля перемещения заголовка).


8
Для простой установки вставок требуется гораздо меньше кода, чем для реализации подкласса. В этом вся суть врезок. Манипулирование фреймами для дополнительных представлений (которые вы не создавали) больше похоже на взлом.
Ким Андре Санд

6
@kimsnarf, правда? Гораздо меньше работы (и меньше взлома) настраивать вставки всякий раз, когда вы вносите незначительные изменения в размер изображения или длину заголовка?
Кирк Уолл

54

Ответ Раймонда В. здесь лучше всего. Подкласс UIButton с настраиваемым layoutSubviews. Чрезвычайно просто сделать, вот реализация layoutSubviews, которая сработала для меня:

- (void)layoutSubviews
{
    // Allow default layout, then adjust image and label positions
    [super layoutSubviews];

    UIImageView *imageView = [self imageView];
    UILabel *label = [self titleLabel];

    CGRect imageFrame = imageView.frame;
    CGRect labelFrame = label.frame;

    labelFrame.origin.x = imageFrame.origin.x;
    imageFrame.origin.x = labelFrame.origin.x + CGRectGetWidth(labelFrame);

    imageView.frame = imageFrame;
    label.frame = labelFrame;
}

2
Этот способ лучше в случае, если вам нужно управлять множеством кнопок, а мне нужно изменить только одну кнопку :)
Павел Якименко

2
Если изображение кнопки равно нулю, результаты метки неуместны, вероятно, из-за того, что UIImageView не вставлен (проверено на iOS6.0). Вам следует рассмотреть возможность редактирования фреймов, только если imageView.image не равен нулю.
Scakko

2
Я бы предложил следующее улучшение этого ответа, чтобы оба представления оставались центрированными: 'CGFloat cumulativeWidth = CGRectGetWidth (imageFrame) + CGRectGetWidth (labelFrame) + 10; CGFloat чрезмерная ширина = CGRectGetWidth (self.bounds) - cumulativeWidth; labelFrame.origin.x = чрезмерная ширина / 2; imageFrame.origin.x = CGRectGetMaxX (labelFrame) + 10; '
i-konov

Для меня это не работает на iOS 7. Кто-нибудь еще? Прекрасно работает на iOS 8.
rounak

1
Не поддерживайте iOS7 вообще, и ваша проблема исчезнет. Вы все равно не должны это подтверждать.
Гил Санд

30

А как насчет подкласса UIButtonи переопределения layoutSubviews?

Затем постобработка местоположений self.imageView&self.titleLabel


4
Это намного проще, чем все остальные советы (например, приведенный выше) о попытке правильно разместить все, используя вручную настроенные вставки.
Стивен Крамер

13

Другой простой способ (это НЕ только для iOS 9) - создать подкласс UIButton, чтобы переопределить эти два метода.

override func titleRectForContentRect(contentRect: CGRect) -> CGRect {
    var rect = super.titleRectForContentRect(contentRect)
    rect.origin.x = 0
    return rect
}

override func imageRectForContentRect(contentRect: CGRect) -> CGRect {
    var rect = super.imageRectForContentRect(contentRect)
    rect.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(rect)
    return rect
}

contentEdgeInsets уже учтено при использовании super.


Спасибо! Мне это нравится намного больше, чем ответы, которые подклассифицируют и переопределяют layoutSubviews () :)
Джо Сусник

1
По моему скромному мнению, лучший способ сделать это :)
DrPatience

9

Использование «справа налево» для кнопки невозможно, если ваше приложение поддерживает как «слева направо», так и «справа налево».

Решение, которое сработало для меня, представляет собой подкласс, который можно добавить к кнопке в раскадровке и который хорошо работает с ограничениями (протестировано в iOS 11):

class ButtonWithImageAtEnd: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        if let imageView = imageView, let titleLabel = titleLabel {
            let padding: CGFloat = 15
            imageEdgeInsets = UIEdgeInsets(top: 5, left: titleLabel.frame.size.width+padding, bottom: 5, right: -titleLabel.frame.size.width-padding)
            titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageView.frame.width, bottom: 0, right: imageView.frame.width)
        }

    }

}

Где «отступ» - это пространство между заголовком и изображением.


Конечно .forceRightToLeftвариант! Просто используйте противоположное значение ( .forceLeftToRight), если UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft.
manmal 06

5

В Swift:

override func layoutSubviews(){
    super.layoutSubviews()

    let inset: CGFloat = 5

    if var imageFrame = self.imageView?.frame,
       var labelFrame = self.titleLabel?.frame {

       let cumulativeWidth = imageFrame.width + labelFrame.width + inset
       let excessiveWidth = self.bounds.width - cumulativeWidth
       labelFrame.origin.x = excessiveWidth / 2
       imageFrame.origin.x = labelFrame.origin.x + labelFrame.width + inset

       self.imageView?.frame = imageFrame
       self.titleLabel?.frame = labelFrame  
    }
}

4

На основе ответа @split ...

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

Например, вы можете захотеть, чтобы изображение имело отступ сверху и снизу контейнера, но все же переместите изображение в правую часть кнопки.

Я расширил концепцию этим методом:

- (void) moveImageToRightSide {
    [self sizeToFit];

    CGFloat titleWidth = self.titleLabel.frame.size.width;
    CGFloat imageWidth = self.imageView.frame.size.width;
    CGFloat gapWidth = self.frame.size.width - titleWidth - imageWidth;
    self.titleEdgeInsets = UIEdgeInsetsMake(self.titleEdgeInsets.top,
                                            -imageWidth + self.titleEdgeInsets.left,
                                            self.titleEdgeInsets.bottom,
                                            imageWidth - self.titleEdgeInsets.right);

    self.imageEdgeInsets = UIEdgeInsetsMake(self.imageEdgeInsets.top,
                                            titleWidth + self.imageEdgeInsets.left + gapWidth,
                                            self.imageEdgeInsets.bottom,
                                            -titleWidth + self.imageEdgeInsets.right - gapWidth);
}

2
// Get the size of the text and image
CGSize buttonLabelSize = [[self.button titleForState:UIControlStateNormal] sizeWithFont:self.button.titleLabel.font];
CGSize buttonImageSize = [[self.button imageForState:UIControlStateNormal] size];

// You can do this line in the xib too:
self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;

// Adjust Edge Insets according to the above measurement. The +2 adds a little space 
self.button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -(buttonLabelSize.width+2));
self.button.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, buttonImageSize.width+2);

Это создает кнопку с выравниванием по правому краю, например:

[           button label (>)]

Ширина кнопки не регулируется в соответствии с контекстом, поэтому слева от метки появится пробел. Вы можете решить эту проблему, вычислив ширину рамки кнопки из buttonLabelSize.width и buttonImageSize.width.



0

Это решение работает с iOS 7 и выше.

Просто подкласс UIButton

@interface UIButton (Image)

- (void)swapTextWithImage;

@end

@implementation UIButton (Image)

- (void)swapTextWithImage {
   const CGFloat kDefaultPadding = 6.0f;
   CGSize buttonSize = [self.titleLabel.text sizeWithAttributes:@{
                                                               NSFontAttributeName:self.titleLabel.font
                                                               }];

   self.titleEdgeInsets = UIEdgeInsetsMake(0, -self.imageView.frame.size.width, 0, self.imageView.frame.size.width);
   self.imageEdgeInsets = UIEdgeInsetsMake(0, buttonSize.width + kDefaultPadding, 0, -buttonSize.width); 
}

@end

Использование (где-то в вашем классе):

[self.myButton setTitle:@"Any text" forState:UIControlStateNormal];
[self.myButton swapTextWithImage];

0

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

let margin = CGFloat(4.0)
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width, 0, button.imageView.frame.size.width);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, -button.titleLabel.frame.size.width)
button.contentEdgeInsets = UIEdgeInsetsMake(0, margin, 0, margin)

Последняя строка кода важна для внутреннего расчета размера содержимого для автоматического макета.


0

Вот мой собственный способ сделать это (примерно через 10 лет)

  1. Подкласс от UIButton (кнопка, поскольку мы живем в эпоху Swift)
  2. Поместите изображение и метку в стек.
class CustomButton: Button {

    var didLayout: Bool = false // The code must be called only once

    override func layoutSubviews() {
        super.layoutSubviews()
        if !didLayout, let imageView = imageView, let titleLabel = titleLabel {
            didLayout = true
            let stack = UIStackView(arrangedSubviews: [titleLabel, imageView])
            addSubview(stack)
            stack.edgesToSuperview() // I use TinyConstraints library. You could handle the constraints directly
            stack.axis = .horizontal
        }
    }
}

0

Однострочное решение в Swift:

// iOS 9 and Onwards
button.semanticContentAttribute = .forceRightToLeft

Это нехорошо, так как нарушит макет для всех пользователей RTL. Вам нужно будет заставить их двигаться слева направо. Вам лучше попробовать другое решение отсюда.
Павел Якименко
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.