在 UIButton 上左对齐图像并居中文本

79

我看到了有关右对齐的帖子,但我无法让左对齐起作用。 我希望按钮占据屏幕的宽度,图像位于左侧,标题/文本位于中心。

以下代码不起作用(至少不可靠):

    button.titleLabel.textAlignment = UITextAlignmentCenter;
    [button setImageEdgeInsets:UIEdgeInsetsMake(0, -60.0, 0, 0)];
    button.frame = CGRectMake((self.view.frame.size.width - w ) / 2, self.view.frame.size.height - 140.0,  self.view.frame.size.width - 10.0, 40.0);
27个回答

76

这个解决方案适用于Swift 3,并且在保持标题标签始终居中可用空间的同时尊重原始内容和图像边框插图,这让调整边距变得更加容易。

它重写了titleRect(forContentRect:)方法并返回正确的框架:

@IBDesignable
class LeftAlignedIconButton: UIButton {
    override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        let titleRect = super.titleRect(forContentRect: contentRect)
        let imageSize = currentImage?.size ?? .zero
        let availableWidth = contentRect.width - imageEdgeInsets.right - imageSize.width - titleRect.width
        return titleRect.offsetBy(dx: round(availableWidth / 2), dy: 0)
    }
}
以下的内容包含插图: enter image description here 在页面上显示为: enter image description here
不推荐使用之前的答案 这种方法在大多数情况下可行,但某些布局会导致layoutSubviews无限递归调用自身,因此请谨慎使用
@IBDesignable
class LeftAlignedIconButton: UIButton {
    override func layoutSubviews() {
        super.layoutSubviews()
        contentHorizontalAlignment = .left
        let availableSpace = UIEdgeInsetsInsetRect(bounds, contentEdgeInsets)
        let availableWidth = availableSpace.width - imageEdgeInsets.right - (imageView?.frame.width ?? 0) - (titleLabel?.frame.width ?? 0)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: availableWidth / 2, bottom: 0, right: 0)
    }
}

这段代码可以实现同样的效果,但是将图标对齐到右边缘:

@IBDesignable
class RightAlignedIconButton: UIButton {
    override func layoutSubviews() {
        super.layoutSubviews()
        semanticContentAttribute = .forceRightToLeft
        contentHorizontalAlignment = .right
        let availableSpace = UIEdgeInsetsInsetRect(bounds, contentEdgeInsets)
        let availableWidth = availableSpace.width - imageEdgeInsets.left - (imageView?.frame.width ?? 0) - (titleLabel?.frame.width ?? 0)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: availableWidth / 2)
    }
}

输入图像描述

右对齐版本使用semanticContentAttribute,因此需要iOS 9或更高版本。


我认为frame.width应该是frame.size.width。 - SArnab
@SArnab frame.width 是正确的 https://developer.apple.com/reference/coregraphics/cgrect/1454758-width - redent84
frame.width 是只读属性,因此我们可以在想要读取帧宽度时使用它 :) - MohyG
7
要使其生效,您需要设置 contentHorizontalAlignment = .left - Radek Wilczak
1
在最后一行:return titleRect.offsetBy(...),我不得不更改为将标题矩形从父矩形移动:return contentRect.offsetBy(...)。 - javi_swift
如果您希望文本在按钮框架中“真正居中”,而忽略图像,请将dx更改为:round((availableWidth - imageSize.width) / 2) - androidguy

52

在我的实现中,我使用了UIEdgeInsetsMake函数,它可以计算左上角的位置使其居中。我不确定是否有什么可以让文本居中对齐的东西,但这对我有用。确保通过计算宽度将左上角设置为所需的位置。例如:UIEdgeInsetsMake(0.0f, 42.0f, 0.0f, 0.0f)

UIButton *scanBarCodeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
scanBarCodeButton.frame = CGRectMake(center, 10.0f, fieldWidth, 40.0f);
[scanBarCodeButton setImage:[UIImage imageNamed:@"BarCodeIcon.png"] forState:UIControlStateNormal];
[scanBarCodeButton setTitle:@"Scan the Barcode" forState:UIControlStateNormal];
scanBarCodeButton.titleEdgeInsets = UIEdgeInsetsMake(0.0f, 42.0f, 0.0f, 0.0f);
[scanBarCodeButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[scanBarCodeButton addTarget:self action:@selector(scanBarCode:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:scanBarCodeButton];

输出结果看起来像这样:

centered text with image

Swift中:

var scanBarCodeButton: UIButton = UIButton(type: .roundedRect)
scanBarCodeButton.frame = CGRectMake(center, 10.0, fieldWidth, 40.0)
scanBarCodeButton.setImage(UIImage(named: "BarCodeIcon.png"), for: UIControlStateNormal)
scanBarCodeButton.setTitle("Scan the Barcode", for: UIControlStateNormal)
scanBarCodeButton.titleEdgeInsets = UIEdgeInsetsMake(0.0, 42.0, 0.0, 0.0)
scanBarCodeButton.contentHorizontalAlignment = .left
scanBarCodeButton.addTarget(self, action: "scanBarCode:", for: UIControlEventTouchUpInside)
self.view.addSubview(scanBarCodeButton)

谢谢!正是我在寻找的。 - David Ansermot
8
对于我来说,重点是将内容的水平对齐设置为左对齐,而不是默认的居中。文本本身仍然在文本区域内居中。这里的“42”是图像的宽度,可以轻松计算。 - Graham Perks
googlebtn.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0);谷歌按钮.图像边缘插入 = 边缘插入(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0); - Abdul Yasin

19

对于Swift 4.0,这里有一个可用的扩展 -

extension UIButton {

  func leftImage(image: UIImage, renderMode: UIImage.RenderingMode) {
       self.setImage(image.withRenderingMode(renderMode), for: .normal)
       self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
       self.titleEdgeInsets.left = (self.frame.width/2) - (self.titleLabel?.frame.width ?? 0)
       self.contentHorizontalAlignment = .left
       self.imageView?.contentMode = .scaleAspectFit
   }
    
    func rightImage(image: UIImage, renderMode: UIImageRenderingMode){
        self.setImage(image.withRenderingMode(renderMode), for: .normal)
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left:image.size.width / 2, bottom: 0, right: 0)
        self.contentHorizontalAlignment = .right
        self.imageView?.contentMode = .scaleAspectFit
    }
}

使用方法:

myButton.rightImage(image: UIImage(named: "image_name")!, renderMode: .alwaysOriginal)
myButton.leftImage(image: UIImage(named: "image_name")!, renderMode: .alwaysOriginal)

renderMode可为.alwaysTemplate.alwaysOriginal。此外,myButton应该是custom类型的UIButton

这个扩展还可以在UIBarButtonItem中的UIButton中使用,在UINavigationBar上使用(注:自iOS 11起,导航栏遵循自动布局,因此您需要添加宽度/高度约束到UIBarButtonItem)。对于在导航栏上的用法,请确保您遵循Apple推荐的@2x和@3x图像大小(即50x50、75x75),并且对于iPhone 6、7、8、6s、7s、8s、Plus变体和iPhone x具有更好的可访问性,UIBarButton的宽度和高度应该是高度-25和宽度-55(或者您的应用程序所需的任何数字,这些数字是大多数情况下都有效的一些基本数字)。


更新:在Swift 4.2中,UIImageRenderingMode已更名为UIImage.RenderingMode

extension UIButton {
    func leftImage(image: UIImage, renderMode: UIImage.RenderingMode) {
        self.setImage(image.withRenderingMode(renderMode), for: .normal)
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: image.size.width / 2)
        self.contentHorizontalAlignment = .left
        self.imageView?.contentMode = .scaleAspectFit
    }
    
    func rightImage(image: UIImage, renderMode: UIImage.RenderingMode){
        self.setImage(image.withRenderingMode(renderMode), for: .normal)
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left:image.size.width / 2, bottom: 0, right: 0)
        self.contentHorizontalAlignment = .right
        self.imageView?.contentMode = .scaleAspectFit
    }
}

这个解决方案必须添加以下代码。 let rightInset = (image.size.width / 2) + 10 self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: rightInset) - Deepak Thakur
6
无效。标题对齐未居中于按钮中,这正是原帖作者想要的。 - Peter Suwara
标题的对齐方式是相对于所使用的图像而言的,因此标题相对于按钮宽度不会居中。如果您将标签相对于按钮宽度居中,则根据标题的长度,某些标题可能会重叠图像。 - Anjan Biswas

12

我已经尝试了上面几乎所有的解决方案,但都不能按照我期望的方式工作(我有两个按钮,每个按钮具有不同的标题和不同的图像宽度)。因此,我编写了自己的UIButton扩展,在处理不同宽度的图像以及不同标题时都非常完美。

extension UIButton {
    func moveImageLeftTextCenter(imagePadding: CGFloat = 30.0, titlePadding: CGFloat = 0.0, minImageTitleDistance: CGFloat = 10.0){
    guard let imageViewWidth = imageView?.frame.width else{return}
    guard let titleLabelWidth = titleLabel?.intrinsicContentSize.width else{return}
    contentHorizontalAlignment = .left

    let imageLeftInset = imagePadding - imageViewWidth / 2
    var titleLeftInset = (bounds.width - titleLabelWidth) / 2 - imageViewWidth + titlePadding

    if titleLeftInset - imageLeftInset < minImageTitleDistance{
        titleLeftInset = imageLeftInset + minImageTitleDistance
    }

    imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imageLeftInset, bottom: 0.0, right: 0.0)
    titleEdgeInsets = UIEdgeInsets(top: 0.0, left: titleLeftInset, bottom: 0.0, right: 0.0)
    }
}

使用方法:myButton.moveImageLeftTextCenter()


9
我写了一个UIButton扩展。
extension UIButton {

  /// Add image on left view
  func leftImage(image: UIImage) {
    self.setImage(image, for: .normal)
    self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: image.size.width)
  }
}

您可以像这样使用:

yourButton.leftImage(image: yourImage)

瞧!


9
[self.button setImage:[UIImage imageNamed:@"image.png"] forState:UIControlStateNormal];

[self.button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, self.button.center.x/2 , 0.0, 0.0)];

[self.button setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];

如果它没有起作用,请告诉我。

我的输出 enter image description here


文本没有居中,而是被图像向右推。 - Shai Balassiano
然后您可以更改UIEdgeInsetsMake的值。试试看吧。 - Deepak Kumar Sahu

9

我认为好的解决方案必须适用于任何文本,不需要针对插图使用硬编码值(这与toytoy提供的解决方案有关)。我使用类似于以下代码:

NSString *sometitle = @"blabla button title";
NSString *someimage = @"blablaimage";
UIImage *image = [[UIImage imageNamed:someimage];
int buttonWidth = A;
int buttonHeight = B;
int imageWidth =image.size.width;
int imageHeight = image.size.height;
int titleWidth = buttonWidth - imageWidth;

// create button and set image and title
buttonView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, buttonWidth, buttonHeight)];
[buttonView setImage:image forState:UIControlStateNormal];
[buttonView setTitle:sometitle forState:UIControlStateNormal];

// make button all content to be left aligned
[buttonView setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];

// set title font 
[buttonView.titleLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:14]];

// calculate font text width with selected font
CGSize stringBoundingBox = [number sizeWithFont:[buttonView.titleLabel font]];

// make title inset with some value from left 
// it place the title right on the center of space for the title
int titleLeft = (titleWidth - stringBoundingBox.width) / 2;
[buttonView setTitleEdgeInsets:UIEdgeInsetsMake(0, titleLeft, 0, 0)];

在使用stringBoundingBox初始化后,我还添加了一些字符串,例如:
if (stringBoundingBox.width > titleWidth)
{
    [_backgroundView.titleLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:13]];
    stringBoundingBox = [number sizeWithFont:[_backgroundView.titleLabel font]];
}
if (stringBoundingBox.width > titleWidth)
{
    [_backgroundView.titleLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:12]];
    stringBoundingBox = [number sizeWithFont:[_backgroundView.titleLabel font]];
}

通过选择一些较小的字体,IT技术允许我设置比可用空间更长的标题。我这样做是因为另一种方法:

[buttonView.titleLabel setAdjustsFontSizeToFitWidth:YES];

工作效果不是很好,看起来字体大小在某些情况下会变小3-4个点,所以有些不是很长的行就变得太小了,比本来应该小。

这段代码可以让我们把任何文本放在按钮标题空间的正中心。


8
创建您的按钮并将文本对齐设置为居中,然后添加:
UIImage *image = [UIImage imageNamed:@"..."];
CGSize imageSize = image.size;
CGFloat offsetY = floor((self.layer.bounds.size.height - imageSize.height) / 2.0);

CALayer *imageLayer = [CALayer layer];
imageLayer.contents = (__bridge id) image.CGImage;
imageLayer.contentsGravity = kCAGravityBottom;
imageLayer.contentsScale = [UIScreen mainScreen].scale;
imageLayer.frame = CGRectMake(offsetY, offsetY, imageSize.width, imageSize.height);
[self.layer addSublayer:imageLayer];

7

尝试了大部分答案都没有成功。大多数情况下,我得到的是蓝色图像,或者标题并不居中。从一些答案中使用代码并编写此扩展程序,完美解决。

Swift 4.2:

import UIKit

extension UIButton {
    func moveImageLeftTextCenter(image : UIImage, imagePadding: CGFloat, renderingMode: UIImage.RenderingMode){
        self.setImage(image.withRenderingMode(renderingMode), for: .normal)
        guard let imageViewWidth = self.imageView?.frame.width else{return}
        guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
        self.contentHorizontalAlignment = .left
        let imageLeft = imagePadding - imageViewWidth / 2
        let titleLeft = (bounds.width - titleLabelWidth) / 2 - imageViewWidth
        imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imageLeft, bottom: 0.0, right: 0.0)
        titleEdgeInsets = UIEdgeInsets(top: 0.0, left: titleLeft , bottom: 0.0, right: 0.0)
    }
}

希望对某些人有所帮助!

对我有效的唯一答案 - mazenqp

5

这对我来说非常有效,适用于具有不同图像宽度和不同标题长度的多个按钮:

创建UIButton的子类,并添加以下方法:

override func layoutSubviews() {
    super.layoutSubviews()

    if let image = imageView?.image {

        let margin = 30 - image.size.width / 2
        let titleRect = titleRectForContentRect(bounds)
        let titleOffset = (bounds.width - titleRect.width - image.size.width - margin) / 2


        contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
            imageEdgeInsets = UIEdgeInsetsMake(0, margin, 0, 0)
            titleEdgeInsets = UIEdgeInsetsMake(0, (bounds.width - titleRect.width -  image.size.width - margin) / 2, 0, 0)
    }

}

@JefferyThomas 这只是一个边距问题。使用这种方法,我可以很好地处理不同大小的图像。 - Rémy Virin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接