Выравнивание текста и изображения на UIButton с помощью imageEdgeInsets и titleEdgeInsets
Я хотел бы поместить значок слева от двух строк текста таким образом, чтобы между изображением и началом текста было около 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)];
но это не отображает его правильно. Я настраивал значения, но переход от say -5 к -10 на левом значении вставки, похоже, не перемещает его ожидаемым образом. -10 будет scoot текст полностью влево, поэтому я ожидал, что -5 будет scoot его на полпути с левой стороны, но это не так.
какая логика стоит за вставками? Я не знаком с размещением изображений и соответствующая терминология.
я использовал этот вопрос SO в качестве ссылки, но что-то о моих ценностях не так.
UIButton: как центрировать изображение и текст с помощью imageEdgeInsets и titleEdgeInsets?
11 ответов:
Я согласен с документацией о
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+Позиция.h
@interface UIButton(ImageTitleCentering) -(void) centerButtonAndImageWithSpacing:(CGFloat)spacing; @endUIButton+Позиция.м
@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];и я получаю то, что мне нужно каждый раз. Нет больше возиться с краевыми вставками вручную.
редактировать: замена изображения и Текст
в ответ на @Javal в комментариях
используя этот же механизм, мы можем менять изображение и текст. Чтобы выполнить своп, просто используйте отрицательный интервал, но также включите ширину текста и изображения. Это потребует, чтобы кадры были известны и макет уже выполнен.
[self.view layoutIfNeeded]; CGFloat flippedSpacing = -(desiredSpacing + button.currentImage.size.width + button.titleLabel.frame.size.width); [button centerButtonAndImageWithSpacing:flippedSpacing];конечно, вы, вероятно, захотите сделать хороший метод для этого, потенциально добавив метод второй категории, это осталось в качестве упражнения для читатель.
я немного опоздал на эту вечеринку, но я думаю, что у меня что-то полезное добавить.
ответ Кекоа велик, но, как упоминает Ронлугге, он может заставить кнопку больше не уважать
sizeToFitили, что более важно, может привести к тому, что кнопка будет обрезать ее содержимое, когда она будет иметь собственный размер. Фу!во-первых, хотя,
краткое объяснение того, как я верю
imageEdgeInsetsиtitleEdgeInsetsработы:The документы для
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 и не хочет вообще думать? Вот окончательная версия расширения в 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) } }
в interface Builder. Выберите инспектор UIButton - > Attributes - > Edge=Title и измените вставки edge
также, если вы хотите сделать что-то подобное
вам нужно
1.Установите горизонтальное и вертикальное выравнивание для кнопки
найти все необходимые значения и набор
UIImageEdgeInsetsCGSize 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) } } } }
вы можете избежать больших неприятностей, используя это --
myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; myButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;это автоматически выровняет все ваше содержимое влево (или туда, где вы хотите)
Swift 3:
myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left; myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center;
на Xcode 8.0 вы можете просто сделать это путем изменения
insetsв инспекторе размера.выберите UIButton - > Attributes Inspector - > перейдите в Инспектор размеров и измените вставки содержимого, изображения и заголовка.
и если вы хотите изменить изображение на правой стороне, вы можете просто изменить семантическое свойство
Force Right-to-leftв инспекторе атрибутов .
Я немного опоздал на эту вечеринку тоже, но я думаю, что у меня есть что-то полезное, чтобы добавить :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:
- создать UIButton
- изменить класс кнопки
- настройка Layoutable атрибутов с помощью "центр", "сверху", "снизу", "слева" или "справа"
![]()
здесь код (суть):
@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() } }
небольшое дополнение к ответу Райли Аврона на изменения локали учетной записи:
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) } }
Swift 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)
вот простой пример, как использовать 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)
элегантный способ в 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) }









Comments