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
Alvivi
источник
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.

DrAL3X
источник
Спасибо! Мне это нравится намного больше, чем ответы, которые подклассифицируют и переопределяют 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)
        }

    }

}

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

Luisma
источник
Конечно .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  
    }
}
ChikabuZ
источник
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);
}
BFar
источник
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.

caliss
источник
1
button.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
Тодд Вандерлин
источник
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. Вам нужно будет заставить их двигаться слева направо. Вам лучше попробовать другое решение отсюда.
Павел Якименко