我如何把图像放在右侧的文本在一个UIButton?

如果可以避免,我不想使用子视图。我想要一个带有背景图像、文本和图像的UIButton。现在,当我这样做的时候,图像在文本的左边。背景图像、文本和图像都有不同的高亮状态。

230599 次浏览

子类化和覆盖layoutSubviews可能是你最好的方法。

引用自:iPhone UIButton -图像位置

我把这个归功于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

子类化UIButton是完全没有必要的。相反,你可以简单地为图像设置一个高的左插入值,为标题设置一个小的右插入值。就像这样:

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

当标题发生变化时,只需更新插图即可。你需要在另一侧用等量的、相反的插页来补偿插页。

[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);

在界面构建器中,您可以为UIButton配置选项边缘插入,分别为三个部分:内容,图像,标题

enter image description here enter image description here

Xcode 8:

enter image description here

扩展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);
}

更新: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
}
}

另外值得注意的是,它处理图像相当好;标题insets。

灵感来自jasongregori的回答;)

把@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)
}
}
}

最简单的解决方案:

iOS 10 &起来,迅速:

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);

iOS 9 &up, Swift: (推荐)

button.semanticContentAttribute = .forceRightToLeft

一个快速的选项,做什么你想要不玩任何插图:

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
}


}
下面是内容居中对齐的UIButton的解决方案。 此代码使图像右对齐,并允许使用imageEdgeInsetstitleEdgeInsets进行珍贵定位

enter image description here

用你的自定义类继承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;
}

这里提到的解决方案停止工作,一旦我启用自动布局。我不得不提出我自己的想法:

子类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;


}

结果:

enter image description here

基于Piotr Tomasik的优雅解决方案:如果你想在按钮标签和image之间也有一点< em >间距,那么将其包括在你的边缘嵌入中,如下所示(复制我的代码,在这里完美地为我工作):

    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的解决方案!

埃里克

从2016年1月开始,所有这些答案都是不必要的。在Interface Builder中,将视图语义设置为Force Right-to-Left,或者如果你喜欢编程方式,设置为semanticContentAttribute = .forceRightToLeft,这将导致图像出现在文本的右侧。

感谢维塔利Gozhenko

我只是想添加,你可以添加IB_DESIGNABLE之前,你的按钮@interface和设置你的按钮类在storyborad。然后你就可以在界面构建阶段实时观看它的布局,而无需启动应用程序

enter image description here

更新XCODE 9(通过接口生成器)

界面构建器中有一个更简单的方法。

选择UIButton并在视图语义中选择此选项:

left-to-right enter image description here 就是这样!很好很简单!< / p >

可选 -第二步:

如果你想调整图像和标题之间的间距,你可以在这里更改形象的插图:

enter image description here

希望有帮助!

斯威夫特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
}
< p > 斯威夫特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
}
如果需要在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)

尽管一些建议的答案非常有创意和聪明,但最简单的解决方案如下:

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

就这么简单。作为奖励,在从右到左的区域中,图像将位于左侧。

编辑:这个问题已经问过几次了,这是iOS 9 +

约束条件呢?与semanticContentAttribute不同,它们不会改变语义。也许是这样的:

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

或者在Objective-C中:

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

警告:未经测试,iOS 9+

由于转换解决方案在iOS 11中不起作用,我决定编写一种新的方法。

调整按钮semanticContentAttribute让我们很好地向右显示图像,而不必在文本更改时重新布局。因此它是理想溶液。然而,我仍然需要RTL的支持。事实上,一个应用程序不能改变它的布局方向在同一会话解决这个问题很容易。

话虽如此,它还是相当直截了当的。

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

要在UIButton内右对齐图像,请尝试下面的代码

btn.contentHorizontalAlignment = .right

扩展的方法

使用扩展设置图像右侧自定义偏移

   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
}
}
在网上尝试了多种解决方案后,我没有达到确切的要求。所以我最终编写了自定义实用程序代码。在将来帮助别人。 在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)
}

做你自己。Xcode10 swift4,

用于编程UI设计

enter image description here

 lazy var buttonFilter : ButtonRightImageLeftTitle = {
var button = ButtonRightImageLeftTitle()
button.setTitle("Playfir", for: UIControl.State.normal)
button.setImage(UIImage(named: "filter"), for: UIControl.State.normal)
button.backgroundColor = UIColor.red
button.contentHorizontalAlignment = .left
button.titleLabel?.font = UIFont.systemFont(ofSize: 16)
return button
}()

边缘插入值应用于矩形以收缩或展开 用矩形表示的面积。通常,使用边缘嵌入 在视图布局期间修改视图的框架。积极的价值观 将被插入(或缩小)指定数量的帧。负 值使帧由指定的对象开始(或展开) 量。< / p >

class ButtonRightImageLeftTitle: UIButton {


override func layoutSubviews() {
super.layoutSubviews()


guard imageView != nil else { return }


imageEdgeInsets = UIEdgeInsets(top: 5, left: (bounds.width - 35), bottom: 5, right: 5)
titleEdgeInsets = UIEdgeInsets(top: 0, left: -((imageView?.bounds.width)! + 10), bottom: 0, right: 0 )


}
}

用于StoryBoard UI设计

enter image description here enter image description here < / p >

对于这个问题,你可以在“label with UIImage view”中创建UIView,并将UIView类设置为UIControl,并创建IBAction为tuch up inside

enter image description here

Swift 4 &5

改变UIButton图像的方向(RTL和LTR)

extension UIButton {
func changeDirection(){
isArabic ? (self.contentHorizontalAlignment = .right) : (self.contentHorizontalAlignment = .left)
// left-right margin
self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 5, bottom: 0, right: 5)
}
}

Xcode 11.4 Swift 5.2

对于任何试图镜像后退按钮样式的人,像这样的雪佛龙:

enter image description here

import UIKit


class NextBarButton: UIBarButtonItem {


convenience init(target: Any, selector: Selector) {


// Create UIButton
let button = UIButton(frame: .zero)


// Set Title
button.setTitle("Next", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 17)


// Configure Symbol
let config = UIImage.SymbolConfiguration(pointSize: 19.0, weight: .semibold, scale: .large)
let image = UIImage(systemName: "chevron.right", withConfiguration: config)
button.setImage(image, for: .normal)


// Add Target
button.addTarget(target, action: selector, for: .touchUpInside)


// Put the Image on the right hand side of the button
// Credit to liau-jian-jie for this part
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)


// Customise spacing to match system Back button
button.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: -18.0, bottom: 0.0, right: 0.0)
button.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -12.0, bottom: 0.0, right: 0.0)


self.init(customView: button)
}
}

实现:

override func viewDidLoad() {
super.viewDidLoad()
let nextButton = NextBarButton(target: self, selector: #selector(nextTapped))
navigationItem.rightBarButtonItem = nextButton
}


@objc func nextTapped() {
// your code
}

我决定不使用标准的按钮图像视图,因为所提出的移动它的解决方案感觉很俗气。这让我获得了想要的美感,并且通过改变约束来重新定位按钮是很直观的:

extension UIButton {
func addRightIcon(image: UIImage) {
let imageView = UIImageView(image: image)
imageView.translatesAutoresizingMaskIntoConstraints = false


addSubview(imageView)


let length = CGFloat(15)
titleEdgeInsets.right += length


NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: self.titleLabel!.trailingAnchor, constant: 10),
imageView.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
imageView.widthAnchor.constraint(equalToConstant: length),
imageView.heightAnchor.constraint(equalToConstant: length)
])
}
}

button with right arrow .

我最终创建了一个自定义按钮,允许从检查器设置图像。下面是我的代码:

import UIKit


@IBDesignable
class CustomButton: UIButton {


@IBInspectable var leftImage: UIImage? = nil
@IBInspectable var gapPadding: CGFloat = 0


override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}


override func layoutSubviews() {
super.layoutSubviews()
setup()
}


func setup() {


if(leftImage != nil) {
let imageView = UIImageView(image: leftImage)
imageView.translatesAutoresizingMaskIntoConstraints = false


addSubview(imageView)


let length = CGFloat(16)
titleEdgeInsets.left += length


NSLayoutConstraint.activate([
imageView.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: gapPadding),
imageView.centerYAnchor.constraint(equalTo: self.titleLabel!.centerYAnchor, constant: 0),
imageView.widthAnchor.constraint(equalToConstant: length),
imageView.heightAnchor.constraint(equalToConstant: length)
])
}
}
}

你可以从检查器中调整空白填充的值来调整文本和图像之间的间距。

PS:使用了一些来自@Mark Hennings答案的代码部分

iOS 15带来了一个更新,现在你可以用一种更简单而不混乱的方式来处理按钮中的图像放置。没有insets。

< p > XIB /故事板: Modify button image placement in editor . 简单地设置按钮的“位置”属性为领先/训练/顶部/底部后,添加一个图像属性按钮。因为它是引导/训练的,所以它支持RTL

有一个额外的优势

**In code (programmically): ** 以编程方式使用“按钮配置”属性 Button configuration, programically . < / p >

这不是向后兼容的特性,只能在iOS15+中工作,正如WWDC '21 - https://developer.apple.com/videos/play/wwdc2021/10064/?time=236所演示的那样

开发人员文档:https://developer.apple.com/documentation/uikit/uibutton/configuration?changes=_4

这是我在swift 5.2上的工作

let sizeOfTitle: CGFloat = 80
let sizeOfImage: CGFloat = 20
yourButton.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -sizeOfImage , bottom: 0.0, right: sizeOfImage)
yourButton.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: sizeOfTitle, bottom: 0.0, right: -sizeOfTitle)

在Xcode 13.3中,我通过以下几个步骤解决了这个问题,并为图像添加了填充。

创建按钮后,按照下面列出的步骤执行:

    首先定义图像:
    let symbol = UIImage(named: "put name of your symbol here")
    
    然后在你创建按钮的viewDidLoad中,初始化上述在1中定义的图像,将图像添加到按钮&设置属性:
    button.setImage(symbol, for: .normal)
    button.semanticContentAttribute = .forceRightToLeft
    button.configuration?.imagePadding = 2
    

不要忘记将按钮添加到视图。