UIButton 中的图像下标签

我试图创建一个按钮,其中有一些文本下的图标(有点像应用程序按钮) ,但它似乎很难实现。有什么想法,我如何去大约得到的文字显示 下面的图像与 UIButton

114736 次浏览

子类 UIButton.Override-layoutSubviews将内置的 subviews移动到新的位置:

- (void)layoutSubviews
{
[super layoutSubviews];


CGRect frame = self.imageView.frame;
frame = CGRectMake(truncf((self.bounds.size.width - frame.size.width) / 2), 0.0f, frame.size.width, frame.size.height);
self.imageView.frame = frame;


frame = self.titleLabel.frame;
frame = CGRectMake(truncf((self.bounds.size.width - frame.size.width) / 2), self.bounds.size.height - frame.size.height, frame.size.width, frame.size.height);
self.titleLabel.frame = frame;
}

在 Xcode,你可以简单地设置边缘标题左插入为负的图像宽度。这将在图像的中心显示标签。

要使标签显示在图像下方(有点像应用程序按钮) ,您可能需要将边缘标题顶部插入设置为某个正数。

编辑: 这里有一些不用 Interface Builder 就能实现的代码:

/// This will move the TitleLabel text of a UIButton to below it's Image and Centered.
/// Note: No point in calling this function before autolayout lays things out.
/// - Parameter padding: Some extra padding to be applied
func centerVertically(padding: CGFloat = 18.0) {
// No point in doing anything if we don't have an imageView size
guard let imageFrame = imageView?.frame else { return }
titleLabel?.numberOfLines = 0
titleEdgeInsets.left = -(imageFrame.width + padding)
titleEdgeInsets.top = (imageFrame.height + padding)
}

请注意,这不会工作,如果你使用自动布局和按钮没有得到布局在屏幕上还没有通过约束。

如果你的子类 UIButtonoverride layoutSubviews,你可以使用下面的图像居中,并将标题居中在下面:

kTextTopPadding是一个你必须引入的常量,它决定了图像和下面文字之间的空间。

-(void)layoutSubviews {
[super layoutSubviews];


// Move the image to the top and center it horizontally
CGRect imageFrame = self.imageView.frame;
imageFrame.origin.y = 0;
imageFrame.origin.x = (self.frame.size.width / 2) - (imageFrame.size.width / 2);
self.imageView.frame = imageFrame;


// Adjust the label size to fit the text, and move it below the image
CGRect titleLabelFrame = self.titleLabel.frame;
CGSize labelSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font
constrainedToSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX)
lineBreakMode:NSLineBreakByWordWrapping];
titleLabelFrame.size.width = labelSize.width;
titleLabelFrame.size.height = labelSize.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.width / 2);
titleLabelFrame.origin.y = self.imageView.frame.origin.y + self.imageView.frame.size.height + kTextTopPadding;
self.titleLabel.frame = titleLabelFrame;


}

这是埃里克 W 的精彩答案的修改版本。但是它不是把图像放在视图的顶部,而是把图像和标签作为一个整体放在视图的中心。

区别在于:

+-----------+
|    ( )    |
|   Hello   |     // Erik W's code
|           |
|           |
+-----------+

+-----------+
|           |
|    ( )    |     // My modified version
|   Hello   |
|           |
+-----------+

资料来源如下:

-(void)layoutSubviews {
[super layoutSubviews];


CGRect titleLabelFrame = self.titleLabel.frame;
CGSize labelSize = [self.titleLabel.text sizeWithFont:self.titleLabel.font constrainedToSize:CGSizeMake(self.frame.size.width, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping];


CGRect imageFrame = self.imageView.frame;


CGSize fitBoxSize = (CGSize){.height = labelSize.height + kTextTopPadding +  imageFrame.size.height, .width = MAX(imageFrame.size.width, labelSize.width)};


CGRect fitBoxRect = CGRectInset(self.bounds, (self.bounds.size.width - fitBoxSize.width)/2, (self.bounds.size.height - fitBoxSize.height)/2);


imageFrame.origin.y = fitBoxRect.origin.y;
imageFrame.origin.x = CGRectGetMidX(fitBoxRect) - (imageFrame.size.width/2);
self.imageView.frame = imageFrame;


// Adjust the label size to fit the text, and move it below the image


titleLabelFrame.size.width = labelSize.width;
titleLabelFrame.size.height = labelSize.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.width / 2);
titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding;
self.titleLabel.frame = titleLabelFrame;
}

供参考: 当与 UIView 动画组合时,这可能会中断,因为在这期间会调用 layoutSubviews。

或者你可以用这个分类:

目的

@interface UIButton (VerticalLayout)


- (void)centerVerticallyWithPadding:(float)padding;
- (void)centerVertically;


@end


@implementation UIButton (VerticalLayout)


- (void)centerVerticallyWithPadding:(float)padding {
CGSize imageSize = self.imageView.frame.size;
CGSize titleSize = self.titleLabel.frame.size;
CGFloat totalHeight = (imageSize.height + titleSize.height + padding);
    

self.imageEdgeInsets = UIEdgeInsetsMake(- (totalHeight - imageSize.height),
0.0f,
0.0f,
- titleSize.width);
    

self.titleEdgeInsets = UIEdgeInsetsMake(0.0f,
- imageSize.width,
- (totalHeight - titleSize.height),
0.0f);
    

self.contentEdgeInsets = UIEdgeInsetsMake(0.0f,
0.0f,
titleSize.height,
0.0f);
}


- (void)centerVertically {
const CGFloat kDefaultPadding = 6.0f;
[self centerVerticallyWithPadding:kDefaultPadding];
}


@end

迅速扩展

extension UIButton {
    

func centerVertically(padding: CGFloat = 6.0) {
guard
let imageViewSize = self.imageView?.frame.size,
let titleLabelSize = self.titleLabel?.frame.size else {
return
}
        

let totalHeight = imageViewSize.height + titleLabelSize.height + padding
        

self.imageEdgeInsets = UIEdgeInsets(
top: -(totalHeight - imageViewSize.height),
left: 0.0,
bottom: 0.0,
right: -titleLabelSize.width
)
        

self.titleEdgeInsets = UIEdgeInsets(
top: 0.0,
left: -imageViewSize.width,
bottom: -(totalHeight - titleLabelSize.height),
right: 0.0
)
        

self.contentEdgeInsets = UIEdgeInsets(
top: 0.0,
left: 0.0,
bottom: titleLabelSize.height,
right: 0.0
)
}
    

}

建议: 如果按钮高度小于 totalHeight,则图像将绘制外部边框。

imageEdgeInset.top应该是:

max(0, -(totalHeight - imageViewSize.height))

下面是我的 UIButton子类,它解决了这个问题:

@implementation MyVerticalButton


@synthesize titleAtBottom; // BOOL property


- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.titleAtBottom = YES;
}
return self;
}


- (CGSize)sizeThatFits:(CGSize)size {
self.titleLabel.text = [self titleForState: self.state];


UIEdgeInsets imageInsets = self.imageEdgeInsets;
UIEdgeInsets titleInsets = self.titleEdgeInsets;


CGSize imageSize = [self imageForState: self.state].size;
if (!CGSizeEqualToSize(imageSize, CGSizeZero)) {
imageSize.width += imageInsets.left + imageInsets.right;
imageSize.height += imageInsets.top + imageInsets.bottom;


}


CGSize textSize = [self.titleLabel sizeThatFits: CGSizeMake(size.width - titleInsets.left - titleInsets.right,
size.height -(imageSize.width +
titleInsets.top+titleInsets.bottom))];
if (!CGSizeEqualToSize(textSize, CGSizeZero)) {
textSize.width += titleInsets.left + titleInsets.right;
textSize.height += titleInsets.top + titleInsets.bottom;
}


CGSize result = CGSizeMake(MAX(textSize.width, imageSize.width),
textSize.height + imageSize.height);
return result;
}


- (void)layoutSubviews {
// needed to update all properities of child views:
[super layoutSubviews];


CGRect bounds = self.bounds;


CGRect titleFrame = UIEdgeInsetsInsetRect(bounds, self.titleEdgeInsets);
CGRect imageFrame = UIEdgeInsetsInsetRect(bounds, self.imageEdgeInsets);
if (self.titleAtBottom) {
CGFloat titleHeight = [self.titleLabel sizeThatFits: titleFrame.size].height;
titleFrame.origin.y = CGRectGetMaxY(titleFrame)-titleHeight;
titleFrame.size.height = titleHeight;
titleFrame = CGRectStandardize(titleFrame);
self.titleLabel.frame = titleFrame;


CGFloat imageBottom = CGRectGetMinY(titleFrame)-(self.titleEdgeInsets.top+self.imageEdgeInsets.bottom);
imageFrame.size.height = imageBottom - CGRectGetMinY(imageFrame);
self.imageView.frame = CGRectStandardize(imageFrame);
} else {
CGFloat titleHeight = [self.titleLabel sizeThatFits: titleFrame.size].height;
titleFrame.size.height = titleHeight;
titleFrame = CGRectStandardize(titleFrame);
self.titleLabel.frame = titleFrame;


CGFloat imageTop = CGRectGetMaxY(titleFrame)+(self.titleEdgeInsets.bottom+self.imageEdgeInsets.top);
imageFrame.size.height = CGRectGetMaxY(imageFrame) - imageTop;
self.imageView.frame = CGRectStandardize(imageFrame);
}
}


- (void)setTitleAtBottom:(BOOL)newTitleAtBottom {
if (titleAtBottom!=newTitleAtBottom) {
titleAtBottom=newTitleAtBottom;
[self setNeedsLayout];
}
}


@end

就是这样。工程喜欢魅力。问题可能会出现,如果按钮将太小,以适应标题和文字。

Dave 在 Swift 中的解决方案是:

override func layoutSubviews() {
super.layoutSubviews()
if let imageView = self.imageView {
imageView.frame.origin.x = (self.bounds.size.width - imageView.frame.size.width) / 2.0
imageView.frame.origin.y = 0.0
}
if let titleLabel = self.titleLabel {
titleLabel.frame.origin.x = (self.bounds.size.width - titleLabel.frame.size.width) / 2.0
titleLabel.frame.origin.y = self.bounds.size.height - titleLabel.frame.size.height
}
}

我认为最好的方法之一是子类化 UIButton 并覆盖一些呈现方法:

- (void)awakeFromNib
{
[super awakeFromNib];
[self setupSubViews];
}


- (instancetype)init
{
if (self = [super init])
{
[self setupSubViews];
}
return self;
}


- (void)setupSubViews
{
[self.imageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addConstraint:[NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.imageView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
[self.titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[imageView][titleLabel]|" options:NSLayoutFormatAlignAllCenterX metrics:nil views:@{@"imageView": self.imageView, @"titleLabel": self.titleLabel}]];
}


- (CGSize)intrinsicContentSize
{
CGSize imageSize = self.imageView.image.size;
CGSize titleSize = [self.titleLabel.text sizeWithAttributes:@{NSFontAttributeName: self.titleLabel.font}];
return CGSizeMake(MAX(imageSize.width, titleSize.width), imageSize.height + titleSize.height);
}

你只需要根据图片和标题标签的大小调整三个边缘插入:

button.contentEdgeInsets = UIEdgeInsetsMake(0, 0, titleLabelBounds.height + 4, 0)
button.titleEdgeInsets = UIEdgeInsetsMake(image.size.height + 8, -image.size.width, 0, 0)
button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -titleLabelBounds.width)

通过在设置标题标签的文本之后调用 sizeToFit,可以获得标题标签的边界。无论文本、字体和图像的大小如何,水平间距都应该起作用,但我不知道有什么单一的解决方案可以使垂直间距和底部内容边缘插入保持一致。

自从 sizeWithFont 在 iOS7中被否定后,更新了 Kenny Winker 的回答。

-(void)layoutSubviews {
[super layoutSubviews];


int kTextTopPadding = 3;


CGRect titleLabelFrame = self.titleLabel.frame;


CGRect labelSize = [self.titleLabel.text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGRectGetHeight(self.bounds)) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:self.titleLabel.font} context:nil];


CGRect imageFrame = self.imageView.frame;


CGSize fitBoxSize = (CGSize){.height = labelSize.size.height + kTextTopPadding +  imageFrame.size.height, .width = MAX(imageFrame.size.width, labelSize.size.width)};


CGRect fitBoxRect = CGRectInset(self.bounds, (self.bounds.size.width - fitBoxSize.width)/2, (self.bounds.size.height - fitBoxSize.height)/2);


imageFrame.origin.y = fitBoxRect.origin.y;
imageFrame.origin.x = CGRectGetMidX(fitBoxRect) - (imageFrame.size.width/2);
self.imageView.frame = imageFrame;


// Adjust the label size to fit the text, and move it below the image


titleLabelFrame.size.width = labelSize.size.width;
titleLabelFrame.size.height = labelSize.size.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.size.width / 2);
titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding;
self.titleLabel.frame = titleLabelFrame;
}

这是在 Swift 中通过覆盖 titleRect(forContentRect:)imageRect(forContentRect:)实现的一个简单的居中标题按钮。它还实现了与 AutoLayout 一起使用的 intrinsicContentSize

import UIKit


class CenteredButton: UIButton
{
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.titleRect(forContentRect: contentRect)


return CGRect(x: 0, y: contentRect.height - rect.height + 5,
width: contentRect.width, height: rect.height)
}


override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.imageRect(forContentRect: contentRect)
let titleRect = self.titleRect(forContentRect: contentRect)


return CGRect(x: contentRect.width/2.0 - rect.width/2.0,
y: (contentRect.height - titleRect.height)/2.0 - rect.height/2.0,
width: rect.width, height: rect.height)
}


override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize


if let image = imageView?.image {
var labelHeight: CGFloat = 0.0


if let size = titleLabel?.sizeThatFits(CGSize(width: self.contentRect(forBounds: self.bounds).width, height: CGFloat.greatestFiniteMagnitude)) {
labelHeight = size.height
}


return CGSize(width: size.width, height: image.size.height + labelHeight + 5)
}


return size
}


override init(frame: CGRect) {
super.init(frame: frame)
centerTitleLabel()
}


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
centerTitleLabel()
}


private func centerTitleLabel() {
self.titleLabel?.textAlignment = .center
}
}

用 Kenny Winker 和 Simeon 的代码,我做了这个对我有用的快速代码。

import UIKit


@IBDesignable
class TopIconButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()


let kTextTopPadding:CGFloat = 3.0;


var titleLabelFrame = self.titleLabel!.frame;




let labelSize = titleLabel!.sizeThatFits(CGSizeMake(CGRectGetWidth(self.contentRectForBounds(self.bounds)), CGFloat.max))


var imageFrame = self.imageView!.frame;


let fitBoxSize = CGSizeMake(max(imageFrame.size.width, labelSize.width), labelSize.height + kTextTopPadding + imageFrame.size.    height)


let fitBoxRect = CGRectInset(self.bounds, (self.bounds.size.width - fitBoxSize.width)/2, (self.bounds.size.height - fitBoxSize.    height)/2);


imageFrame.origin.y = fitBoxRect.origin.y;
imageFrame.origin.x = CGRectGetMidX(fitBoxRect) - (imageFrame.size.width/2);
self.imageView!.frame = imageFrame;


// Adjust the label size to fit the text, and move it below the image


titleLabelFrame.size.width = labelSize.width;
titleLabelFrame.size.height = labelSize.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (labelSize.width / 2);
titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding;
self.titleLabel!.frame = titleLabelFrame;
self.titleLabel!.textAlignment = .Center
}


}

看看这个用 Swift 写的 答得好

extension UIButton {


func alignImageAndTitleVertically(padding: CGFloat = 6.0) {
let imageSize = self.imageView!.frame.size
let titleSize = self.titleLabel!.frame.size
let totalHeight = imageSize.height + titleSize.height + padding


self.imageEdgeInsets = UIEdgeInsets(
top: -(totalHeight - imageSize.height),
left: 0,
bottom: 0,
right: -titleSize.width
)


self.titleEdgeInsets = UIEdgeInsets(
top: 0,
left: -imageSize.width,
bottom: -(totalHeight - titleSize.height),
right: 0
)
}


}

下面是 Swift 2.0中的一个子类“容忍我”的答案。要使用它,只需将 Interface Builder中的按钮类更改为 VerticalButton,它就会神奇地更新预览。

我还对它进行了更新,以计算用于自动布局的正确的内部内容大小。

import UIKit


@IBDesignable


class VerticalButton: UIButton {
@IBInspectable var padding: CGFloat = 8


override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()


update()
}


override func layoutSubviews() {
super.layoutSubviews()


update()
}


func update() {
let imageBounds = self.imageView!.bounds
let titleBounds = self.titleLabel!.bounds
let totalHeight = CGRectGetHeight(imageBounds) + padding + CGRectGetHeight(titleBounds)


self.imageEdgeInsets = UIEdgeInsets(
top: -(totalHeight - CGRectGetHeight(imageBounds)),
left: 0,
bottom: 0,
right: -CGRectGetWidth(titleBounds)
)


self.titleEdgeInsets = UIEdgeInsets(
top: 0,
left: -CGRectGetWidth(imageBounds),
bottom: -(totalHeight - CGRectGetHeight(titleBounds)),
right: 0
)
}


override func intrinsicContentSize() -> CGSize {
let imageBounds = self.imageView!.bounds
let titleBounds = self.titleLabel!.bounds


let width = CGRectGetWidth(imageBounds) > CGRectGetWidth(titleBounds) ? CGRectGetWidth(imageBounds) : CGRectGetWidth(titleBounds)
let height = CGRectGetHeight(imageBounds) + padding + CGRectGetHeight(titleBounds)


return CGSizeMake(width, height)
}
}

UIButton子类中类似这样的东西

public override func layoutSubviews() {
super.layoutSubviews()


imageEdgeInsets = UIEdgeInsetsMake(-10, 0, 0, 0)
titleEdgeInsets = UIEdgeInsetsMake(0, -bounds.size.width/2 - 10, -30, 0)
}

@ 蒂亚戈,我把你的答案改成这样,它适合各种尺寸

func alignImageAndTitleVertically(padding: CGFloat = 5.0) {
self.sizeToFit()
let imageSize = self.imageView!.frame.size
let titleSize = self.titleLabel!.frame.size
let totalHeight = imageSize.height + titleSize.height + padding


self.imageEdgeInsets = UIEdgeInsets(
top: -(totalHeight - imageSize.height),
left: 0,
bottom: 0,
right: -titleSize.width
)


self.titleEdgeInsets = UIEdgeInsets(
top: 0,
left: 0,
bottom: -(totalHeight - titleSize.height),
right: titleSize.height
)
}

很简单。

而不是这样:

   button.setImage(UIImage(named: "image"), forState: .Normal)

用这个:

   button.setBackgroundImage(UIImage(named: "image", forState: .Normal)

然后你可以很容易地在按钮上添加文本:

//button.titleLabel! . font = UIFont (name: “ FontName”,size: 30)

 button.setTitle("TitleText", forState: UIControlState.Normal)

我把这里的答案组合起来,想出了一个似乎对我有用的答案,用 Swift。我不喜欢我覆盖了插图,但是很管用。我愿意接受评论中提出的改进建议。它似乎与 sizeToFit()和自动布局正确工作。

import UIKit


/// A button that displays an image centered above the title.  This implementation
/// only works when both an image and title are set, and ignores
/// any changes you make to edge insets.
class CenteredButton: UIButton
{
let padding: CGFloat = 0.0


override func layoutSubviews() {
if imageView?.image != nil && titleLabel?.text != nil {
let imageSize: CGSize = imageView!.image!.size
titleEdgeInsets = UIEdgeInsetsMake(0.0, -imageSize.width, -(imageSize.height + padding), 0.0)
let labelString = NSString(string: titleLabel!.text!)
let titleSize = labelString.sizeWithAttributes([NSFontAttributeName: titleLabel!.font])
imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + padding), 0.0, 0.0, -titleSize.width)
let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0)
}
super.layoutSubviews()
}


override func sizeThatFits(size: CGSize) -> CGSize {
let defaultSize = super.sizeThatFits(size)
if let titleSize = titleLabel?.sizeThatFits(size),
let imageSize = imageView?.sizeThatFits(size) {
return CGSize(width: ceil(max(imageSize.width, titleSize.width)), height: ceil(imageSize.height + titleSize.height + padding))
}
return defaultSize
}


override func intrinsicContentSize() -> CGSize {
let size = sizeThatFits(CGSize(width: CGFloat.max, height: CGFloat.max))
return size
}
}

这里更正了其中一个答案:

斯威夫特3:

class CenteredButton: UIButton
{
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.titleRect(forContentRect: contentRect)
let imageRect = super.imageRect(forContentRect: contentRect)


return CGRect(x: 0, y: imageRect.maxY + 10,
width: contentRect.width, height: rect.height)
}


override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.imageRect(forContentRect: contentRect)
let titleRect = self.titleRect(forContentRect: contentRect)


return CGRect(x: contentRect.width/2.0 - rect.width/2.0,
y: (contentRect.height - titleRect.height)/2.0 - rect.height/2.0,
width: rect.width, height: rect.height)
}


override init(frame: CGRect) {
super.init(frame: frame)
centerTitleLabel()
}


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
centerTitleLabel()
}


private func centerTitleLabel() {
self.titleLabel?.textAlignment = .center
}
}

@ simeon 的 解决方案在 Objective-C

#import "CenteredButton.h"


@implementation CenteredButton


- (CGRect)titleRectForContentRect:(CGRect)contentRect
{
CGRect rect = [super titleRectForContentRect: contentRect];
return CGRectMake(0,
contentRect.size.height - rect.size.height - 5,
contentRect.size.width,
rect.size.height);
}


- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
CGRect rect = [super imageRectForContentRect: contentRect];
CGRect titleRect = [self titleRectForContentRect: contentRect];


return CGRectMake(contentRect.size.width / 2.0 - rect.size.width / 2.0,
(contentRect.size.height - titleRect.size.height)/2.0 - rect.size.height/2.0,
rect.size.width,
rect.size.height);
}


- (CGSize)intrinsicContentSize {
CGSize imageSize = [super intrinsicContentSize];


if (self.imageView.image) {
UIImage* image = self.imageView.image;
CGFloat labelHeight = 0.0;


CGSize labelSize = [self.titleLabel sizeThatFits: CGSizeMake([self contentRectForBounds: self.bounds].size.width, CGFLOAT_MAX)];
if (CGSizeEqualToSize(imageSize, labelSize)) {
labelHeight = imageSize.height;
}


return CGSizeMake(MAX(labelSize.width, imageSize.width), image.size.height + labelHeight + 5);
}


return imageSize;
}


- (id) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self centerTitleLabel];
}
return self;


}


- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self centerTitleLabel];
}
return self;
}


- (void)centerTitleLabel {
self.titleLabel.textAlignment = NSTextAlignmentCenter;
}


@end

重组冰晶23的答案。

Swift 3,使用自动布局,xib,故事板,可以制作动画。

按钮在原始冰晶23的答案有一个糟糕的计算框架。我想我修正了。

编辑: 更新到 Swift 5并在 Interface Builder/故事板内部完成工作

编辑: 根据 Alexander Akers 对下面答案的评论更新以支持本地化(ltr/rtl)

import UIKit


@IBDesignable
class VerticalButton: UIButton {


@IBInspectable public var padding: CGFloat = 20.0 {
didSet {
setNeedsLayout()
}
}
    

override var intrinsicContentSize: CGSize {
let maxSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude)
        

if let titleSize = titleLabel?.sizeThatFits(maxSize), let imageSize = imageView?.sizeThatFits(maxSize) {
let width = ceil(max(imageSize.width, titleSize.width))
let height = ceil(imageSize.height + titleSize.height + padding)
            

return CGSize(width: width, height: height)
}
        

return super.intrinsicContentSize
}
    

override func layoutSubviews() {
if let image = imageView?.image, let title = titleLabel?.attributedText {
let imageSize = image.size
let titleSize = title.size()
            

if effectiveUserInterfaceLayoutDirection == .leftToRight {
titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: -(imageSize.height + padding), right: 0.0)
imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + padding), left: 0.0, bottom: 0.0, right: -titleSize.width)
}
else {
titleEdgeInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: -(imageSize.height + padding), right: -imageSize.width)
imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + padding), left: -titleSize.width, bottom: 0.0, right: 0.0)
}
}
        

super.layoutSubviews()
}


}

IOS 11-Objective-C

-(void)layoutSubviews {
[super layoutSubviews];


CGRect titleLabelFrame = self.titleLabel.frame;
CGSize labelSize = [self.titleLabel.text sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:12.0f]}];
CGSize adjustedLabelSize = CGSizeMake(ceilf(labelSize.width), ceilf(labelSize.height));


CGRect imageFrame = self.imageView.frame;


CGSize fitBoxSize = (CGSize){.height = adjustedLabelSize.height + kTextTopPadding +  imageFrame.size.height, .width = MAX(imageFrame.size.width, adjustedLabelSize.width)};


CGRect fitBoxRect = CGRectInset(self.bounds, (self.bounds.size.width - fitBoxSize.width)/2, (self.bounds.size.height - fitBoxSize.height)/2);


imageFrame.origin.y = fitBoxRect.origin.y;
imageFrame.origin.x = CGRectGetMidX(fitBoxRect) - (imageFrame.size.width/2);
self.imageView.frame = imageFrame;


// Adjust the label size to fit the text, and move it below the image


titleLabelFrame.size.width = adjustedLabelSize.width;
titleLabelFrame.size.height = adjustedLabelSize.height;
titleLabelFrame.origin.x = (self.frame.size.width / 2) - (adjustedLabelSize.width / 2);
titleLabelFrame.origin.y = fitBoxRect.origin.y + imageFrame.size.height + kTextTopPadding;
self.titleLabel.frame = titleLabelFrame; }

我发现 Simeon 的回答可能是最好的,但是它在一些按钮上给了我奇怪的结果,我就是不明白为什么。因此,以他的回答为基础,我实现了如下按钮:

#define PADDING 2.0f


@implementation OOButtonVerticalImageText


-(CGSize) intrinsicContentSize {
CGSize size = [super intrinsicContentSize];
CGFloat labelHeight = 0.0f;
CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake([self contentRectForBounds:self.bounds].size.width, CGFLOAT_MAX)];
labelHeight = titleSize.height;
return CGSizeMake(MAX(titleSize.width, self.imageView.image.size.width), self.imageView.image.size.height + labelHeight + PADDING);
}


-(void) layoutSubviews {
[super layoutSubviews];


CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake([self contentRectForBounds:self.bounds].size.width, CGFLOAT_MAX)];
self.titleLabel.frame = CGRectMake((self.bounds.size.width - titleSize.width)/2.0f,
self.bounds.size.height - titleSize.height - PADDING,
titleSize.width,
titleSize.height);


CGSize ivSize = self.imageView.frame.size;
self.imageView.frame = CGRectMake((self.bounds.size.width - ivSize.width)/2.0f,
self.titleLabel.frame.origin.y - ivSize.height - PADDING,
ivSize.width,
ivSize.height);
}


@end

在 iOS11/Swift 4上面的答案对我来说都不管用。我找到了一些例子,并加以阐述:

extension UIButton {


func centerImageAndButton(_ gap: CGFloat, imageOnTop: Bool) {


guard let imageView = self.currentImage,
let titleLabel = self.titleLabel?.text else { return }


let sign: CGFloat = imageOnTop ? 1 : -1
self.titleEdgeInsets = UIEdgeInsetsMake((imageView.size.height + gap) * sign, -imageView.size.width, 0, 0);


let titleSize = titleLabel.size(withAttributes:[NSAttributedStringKey.font: self.titleLabel!.font!])
self.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + gap) * sign, 0, 0, -titleSize.width)
}
}

希望这对谁有帮助。

如果您使用的是 自定义字体,那么 titleLabel 大小的计算将不能正常工作,您应该将其替换为

let titleLabelSize = self.titleLabel?.text?.size(withAttributes: [NSAttributedStringKey.font: self.titleLabel!.font!])

使用以下两种方法:

func titleRect(forContentRect contentRect: CGRect) -> CGRect
func imageRect(forContentRect contentRect: CGRect) -> CGRect

例如:

class VerticalButton: UIButton {


override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
let titleRect = super.titleRect(forContentRect: contentRect)
let imageRect = super.imageRect(forContentRect: contentRect)


return CGRect(x: 0,
y: contentRect.height - (contentRect.height - padding - imageRect.size.height - titleRect.size.height) / 2 - titleRect.size.height,
width: contentRect.width,
height: titleRect.height)
}


override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
let imageRect = super.imageRect(forContentRect: contentRect)
let titleRect = self.titleRect(forContentRect: contentRect)


return CGRect(x: contentRect.width/2.0 - imageRect.width/2.0,
y: (contentRect.height - padding - imageRect.size.height - titleRect.size.height) / 2,
width: imageRect.width,
height: imageRect.height)
}


private let padding: CGFloat
init(padding: CGFloat) {
self.padding = padding


super.init(frame: .zero)
self.titleLabel?.textAlignment = .center
}


required init?(coder aDecoder: NSCoder) { fatalError() }
}


extension UIButton {


static func vertical(padding: CGFloat) -> UIButton {
return VerticalButton(padding: padding)
}
}

你可以使用:

let myButton = UIButton.vertical(padding: 6)

你可以创建一个 NSAttributedString,把你的图片作为一个附件,然后把它设置为你按钮的属性标题,而不是费力地用边缘插入来定位图标和文本:

let titleText = NSAttributedString(string: yourTitle, attributes: attributes)
let imageAttachment = NSTextAttachment()
imageAttachment.image = yourImage


let title = NSMutableAttributedString(string: "")
title.append(NSAttributedString(attachment: imageAttachment))
title.append(titleText)


button.setAttributedTitle(title, for: .normal)

本地化友好解决方案:

这么多伟大的解决方案的家伙,但我想在这里添加一个注意,谁使用本地化。

您需要反转 EdgeInstets 的左右两个值,以便在 Lt 到 RtL 的语言方向发生变化的情况下获得正确布局的按钮。

使用类似的解决方案,我会实现如下:

extension UIButton {


func alignVertical(spacing: CGFloat, lang: String) {
guard let imageSize = self.imageView?.image?.size,
let text = self.titleLabel?.text,
let font = self.titleLabel?.font
else { return }


let labelString = NSString(string: text)
let titleSize = labelString.size(
withAttributes: [NSAttributedString.Key.font: font]
)


var titleLeftInset: CGFloat = -imageSize.width
var titleRigtInset: CGFloat = 0.0


var imageLeftInset: CGFloat = 0.0
var imageRightInset: CGFloat = -titleSize.width


if Locale.current.languageCode! != "en" { // If not Left to Right language
titleLeftInset = 0.0
titleRigtInset = -imageSize.width


imageLeftInset = -titleSize.width
imageRightInset = 0.0
}


self.titleEdgeInsets = UIEdgeInsets(
top: 0.0,
left: titleLeftInset,
bottom: -(imageSize.height + spacing),
right: titleRigtInset
)
self.imageEdgeInsets = UIEdgeInsets(
top: -(titleSize.height + spacing),
left: imageLeftInset,
bottom: 0.0,
right: imageRightInset
)
let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
self.contentEdgeInsets = UIEdgeInsets(
top: edgeOffset,
left: 0.0,
bottom: edgeOffset,
right: 0.0
)
}
}


迅猛5 - 下面的方法对我有用

func centerVerticallyWithPadding(padding : CGFloat) {
guard
let imageViewSize = self.imageView?.frame.size,
let titleLabelSize = self.titleLabel?.frame.size else {
return
}


let totalHeight = imageViewSize.height + titleLabelSize.height + padding


self.imageEdgeInsets = UIEdgeInsets(
top: max(0, -(totalHeight - imageViewSize.height)),
left: 0.0,
bottom: 0.0,
right: -titleLabelSize.width
)


self.titleEdgeInsets = UIEdgeInsets(
top: (totalHeight - imageViewSize.height),
left: -imageViewSize.width,
bottom: -(totalHeight - titleLabelSize.height),
right: 0.0
)


self.contentEdgeInsets = UIEdgeInsets(
top: 0.0,
left: 0.0,
bottom: titleLabelSize.height,
right: 0.0
)
}

确保你的按钮标题在故事板/xib else 中没有被截断
解决方案2

class SVVerticalButton: UIButton {


override func layoutSubviews() {
super.layoutSubviews()
let padding : CGFloat = 2.0
if let imageView = self.imageView {
imageView.frame.origin.x = (self.bounds.size.width - imageView.frame.size.width) / 2.0
imageView.frame.origin.y = max(0,(self.bounds.size.height - (imageView.frame.size.height + (titleLabel?.frame.size.height ?? 0.0) + padding)) / 2.0)
}
if let titleLabel = self.titleLabel {
titleLabel.frame.origin.x = 0
titleLabel.frame.origin.y = self.bounds.size.height - titleLabel.frame.size.height
titleLabel.frame.size.width = self.bounds.size.width
titleLabel.textAlignment = .center
}
}


}

顶部图像和底部标题按钮与子类 UIButton

class VerticalButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
let padding: CGFloat = 8
let iH = imageView?.frame.height ?? 0
let tH = titleLabel?.frame.height ?? 0
let v: CGFloat = (frame.height - iH - tH - padding) / 2
if let iv = imageView {
let x = (frame.width - iv.frame.width) / 2
iv.frame.origin.y = v
iv.frame.origin.x = x
}


if let tl = titleLabel {
let x = (frame.width - tl.frame.width) / 2
tl.frame.origin.y = frame.height - tl.frame.height - v
tl.frame.origin.x = x
}
}
}

对于这个问题来说,这确实有点夸张了,但是..。 在我的一个项目中,我首先必须实现一个图标最左对齐的按钮。然后我们得到另一个按钮与标题下的形象。我寻找一个现有的解决方案,但没有运气 所以,这里有一个对齐按钮:

@IBDesignable
class AlignableButton: UIButton {


override class var requiresConstraintBasedLayout: Bool {
return true
}


@objc enum IconAlignment: Int {
case top, left, right, bottom
}


// MARK: - Designables
@IBInspectable var iconAlignmentValue: Int {
set {
iconAlignment = IconAlignment(rawValue: newValue) ?? .left
}
get {
return iconAlignment.rawValue
}
}


var iconAlignment: IconAlignment = .left


@IBInspectable var titleAlignmentValue: Int {
set {
titleAlignment = NSTextAlignment(rawValue: newValue) ?? .left
}
get {
return titleAlignment.rawValue
}
}


var titleAlignment: NSTextAlignment = .left


// MARK: - Corner Radius
@IBInspectable
var cornerRadius: CGFloat {
get {
return layer.cornerRadius
}
set {
layer.masksToBounds = (newValue != 0)
layer.cornerRadius = newValue
}
}


// MARK: - Content size
override var intrinsicContentSize: CGSize {
    

switch iconAlignment {
case .top, .bottom:
return verticalAlignedIntrinsicContentSize
    

default:
return super.intrinsicContentSize
}
}


private var verticalAlignedIntrinsicContentSize: CGSize {
    

if let imageSize = imageView?.intrinsicContentSize,
let labelSize = titleLabel?.intrinsicContentSize {
        

let width = max(imageSize.width, labelSize.width) + contentEdgeInsets.left + contentEdgeInsets.right
let height = imageSize.height + labelSize.height + contentEdgeInsets.top + contentEdgeInsets.bottom
        

return CGSize(
width: ceil(width),
height: ceil(height)
)
}
    

return super.intrinsicContentSize
}


// MARK: - Image Rect
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
    

switch iconAlignment {
case .top:
return topAlignedImageRect(forContentRect: contentRect)
case .bottom:
return bottomAlignedImageRect(forContentRect: contentRect)
case .left:
return leftAlignedImageRect(forContentRect: contentRect)
case .right:
return rightAlignedImageRect(forContentRect: contentRect)
}
}


func topAlignedImageRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.imageRect(forContentRect: contentRect)
    

let x = (contentRect.width - rect.width) / 2.0 + contentRect.minX
let y = contentRect.minY
let w = rect.width
let h = rect.height
    

return CGRect(
x: x,
y: y,
width: w,
height: h
).inset(by: imageEdgeInsets)
}


func bottomAlignedImageRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.imageRect(forContentRect: contentRect)
    

let x = (contentRect.width - rect.width) / 2.0 + contentRect.minX
let y = contentRect.height - rect.height + contentRect.minY
let w = rect.width
let h = rect.height
    

return CGRect(
x: x,
y: y,
width: w,
height: h
).inset(by: imageEdgeInsets)
}


func leftAlignedImageRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.imageRect(forContentRect: contentRect)
    

let x = contentRect.minX
let y = (contentRect.height - rect.height) / 2 + contentRect.minY
let w = rect.width
let h = rect.height
    

return CGRect(
x: x,
y: y,
width: w,
height: h
).inset(by: imageEdgeInsets)
}


func rightAlignedImageRect(forContentRect contentRect: CGRect) -> CGRect {
let rect = super.imageRect(forContentRect: contentRect)
    

let x = (contentRect.width - rect.width) + contentRect.minX
let y = (contentRect.height - rect.height) / 2 + contentRect.minY
let w = rect.width
let h = rect.height
    

return CGRect(
x: x,
y: y,
width: w,
height: h
).inset(by: imageEdgeInsets)
}


// MARK: - Title Rect
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
    

switch iconAlignment {
case .top:
return topAlignedTitleRect(forContentRect: contentRect)
case .bottom:
return bottomAlignedTitleRect(forContentRect: contentRect)
case .left:
return leftAlignedTitleRect(forContentRect: contentRect)
case .right:
return rightAlignedTitleRect(forContentRect: contentRect)
}
}


func topAlignedTitleRect(forContentRect contentRect: CGRect) -> CGRect {
    

let rect = super.titleRect(forContentRect: contentRect)


let x = contentRect.minX
let y = contentRect.height - rect.height + contentRect.minY
let w = contentRect.width
let h = rect.height
    

return CGRect(
x: x,
y: y,
width: w,
height: h
).inset(by: titleEdgeInsets)
}


func bottomAlignedTitleRect(forContentRect contentRect: CGRect) -> CGRect {
    

let rect = super.titleRect(forContentRect: contentRect)
    

let x = contentRect.minX
let y = contentRect.minY
let w = contentRect.width
let h = rect.height
    

return CGRect(
x: x,
y: y,
width: w,
height: h
).inset(by: titleEdgeInsets)
}


func leftAlignedTitleRect(forContentRect contentRect: CGRect) -> CGRect {
    

let titleRect = super.titleRect(forContentRect: contentRect)
let imageRect = self.imageRect(forContentRect: contentRect)
    

let x = imageRect.width + imageRect.minX
let y = (contentRect.height - titleRect.height) / 2.0 + contentRect.minY
let w = contentRect.width - imageRect.width * 2.0
let h = titleRect.height
    

return CGRect(
x: x,
y: y,
width: w,
height: h
).inset(by: titleEdgeInsets)
}


func rightAlignedTitleRect(forContentRect contentRect: CGRect) -> CGRect {
    

let titleRect = super.titleRect(forContentRect: contentRect)
let imageRect = self.imageRect(forContentRect: contentRect)


let x = contentRect.minX + imageRect.width
let y = (contentRect.height - titleRect.height) / 2.0 + contentRect.minY
let w = contentRect.width - imageRect.width * 2.0
let h = titleRect.height
    

return CGRect(
x: x,
y: y,
width: w,
height: h
).inset(by: titleEdgeInsets)
}


// MARK: - Lifecycle
override func awakeFromNib() {
super.awakeFromNib()
    

titleLabel?.textAlignment = titleAlignment
}


override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
    

titleLabel?.textAlignment = titleAlignment
}
}

希望对你有用。

Swift 5,自动版式

我实现了一些方法的组合,这对我来说效果最好。技巧是用这些视图的计算值覆盖 titleRect(forContentRect:)imageRect(forContentRect:)方法和 intrinsicContentSize getter。

结果:

enter image description here

final class CustomButton: UIButton {


override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
let superRect = super.titleRect(forContentRect: contentRect)
return CGRect(
x: 0,
y: contentRect.height - superRect.height,
width: contentRect.width,
height: superRect.height
)
}


override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
let superRect = super.imageRect(forContentRect: contentRect)
return CGRect(
x: contentRect.width / 2 - superRect.width / 2,
y: (contentRect.height - titleRect(forContentRect: contentRect).height) / 2 - superRect.height / 2,
width: superRect.width,
height: superRect.height
)
}


override var intrinsicContentSize: CGSize {
_ = super.intrinsicContentSize
guard let image = imageView?.image else { return super.intrinsicContentSize }
let size = titleLabel?.sizeThatFits(contentRect(forBounds: bounds).size) ?? .zero
let spacing: CGFloat = 12
return CGSize(width: max(size.width, image.size.width), height: image.size.height + size.height + spacing)
}


override init(frame: CGRect) {
super.init(frame: frame)
setup()
}


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}


private func setup() {
titleLabel?.textAlignment = .center
}
}

上述解决方案可能不适用于 iOS12或更老版本。在这种情况下,根据字体大小创建具有固定图像大小和标签高度的常量。

final class CustomButton: UIButton {


private enum Constants {
static let imageSize: CGFloat = 48
static let titleHeight = yourFont.pointSize - yourFont.descender
}


override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
_ = super.titleRect(forContentRect: contentRect)
return CGRect(
x: 0,
y: contentRect.height - Constants.titleHeight,
width: contentRect.width,
height: Constants.titleHeight
)
}


override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
return CGRect(
x: contentRect.width / 2 - Constants.imageSize / 2,
y: (contentRect.height - titleRect(forContentRect: contentRect).height) / 2 - Constants.imageSize / 2,
width: Constants.imageSize,
height: Constants.imageSize
)
}


override var intrinsicContentSize: CGSize {
_ = super.intrinsicContentSize
let size = titleLabel?.sizeThatFits(contentRect(forBounds: bounds).size) ?? .zero
let spacing: CGFloat = 12
return CGSize(
width: max(size.width, Constants.imageSize),
height: Constants.imageSize + Constants.titleHeight + spacing
)
}


override init(frame: CGRect) {
super.init(frame: frame)
setup()
}


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}


private func setup() {
titleLabel?.textAlignment = .center
}
}

我发现了另一种方法:

UIButton 有2个 UIImageView。首先,我们可以使用 UIButton 的 imageView 属性来实现它们。另一个 UIImage 视图不能通过 UIButton 的属性访问,但是另一个 UIImageView 非常适合实现居中按钮。为了进入背景图像视图,我添加了一个扩展:

extension UIButton {


var backgroundImageView: UIImageView? {
return subviews.first(where: { $0.isKind(of: UIImageView.self) && $0 != imageView }) as? UIImageView
}
}

下一步是设置按钮:

centeredButton.setBackgroundImage(perfectImage, for: .normal) // This sets image to Button's backgroundImageView
centeredButton.backgroundImageView?.contentMode = .top // This moves image to the top of the Button's frame
centeredButton.contentVerticalAlignment = .bottom // This moves label to the bottom of the Button's frame.

下一步是在图像和标签之间设置填充。可以通过向按钮添加高度约束来实现这一点。图像总是在顶部,标签在底部。

在 IOS 15上使用@Robert Dresler 解决方案不起作用,所以我通过添加一个条件来扩展按钮类,以检查是否是 IOS 15并使用 新的用于 IOS 15的 ui 按钮配置

final class ImageButton: UIButton {


private enum Constants {
static let imageSize: CGFloat = 40
static let titleHeight: CGFloat = 12
}


override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
if #available(iOS 15, *) {
return super.titleRect(forContentRect: contentRect)
}
else {
_ = super.titleRect(forContentRect: contentRect)
return CGRect(
x: 0,
y: contentRect.height - Constants.titleHeight,
width: contentRect.width,
height: Constants.titleHeight
)
}
}


override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
if #available(iOS 15, *) {
return super.imageRect(forContentRect: contentRect)
} else {
return CGRect(
x: contentRect.width / 2 - Constants.imageSize / 2,
y: (contentRect.height - titleRect(forContentRect: contentRect).height) / 2 - Constants.imageSize / 2,
width: Constants.imageSize,
height: Constants.imageSize
)
}
}


override var intrinsicContentSize: CGSize {
if #available(iOS 15, *) {
return super.intrinsicContentSize
}
else {
_ = super.intrinsicContentSize
let size = titleLabel?.sizeThatFits(contentRect(forBounds: bounds).size) ?? .zero
let spacing: CGFloat = 12
return CGSize(
width: max(size.width, Constants.imageSize),
height: Constants.imageSize + Constants.titleHeight + spacing
)
}
}


override init(frame: CGRect) {
super.init(frame: frame)
setup()
}


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}


private func setup() {
if #available(iOS 15, *) {
var myConfiguration = UIButton.Configuration.plain()
myConfiguration.imagePlacement = .top
self.configuration = myConfiguration
} else {
titleLabel?.textAlignment = .center
}
}

}

这个解决方案适用于 IOS 12到15

这是最终结果的快照

Result

故事板友好解决方案

这里有一个方法可以让插图在故事板中得到进一步的调整:

@IBDesignable
class RotatableButton: UIButton {
@IBInspectable public var rotate: Bool = false {
didSet { if (rotate) { Rotate() } }
}


func Rotate() {
self.titleLabel?.transform = CGAffineTransform(rotationAngle: CGFloat(-CGFloat.pi/2))
self.transform = CGAffineTransform(rotationAngle: CGFloat(CGFloat.pi/2))
// Hacky workaround text clipping
self.titleLabel?.lineBreakMode = .byWordWrapping
self.setTitle((self.titleLabel?.text)! + "\n\n", for: .normal)
}
}