我想在两行文本的左边放置一个图标,这样在图像和文本开始之间有大约2-3像素的空间。控件本身水平居中对齐(通过接口生成器设置)

这个按钮应该是这样的:

|                  |
|[Image] Add To    |
|        Favorites |

我试图配置这与contenttedgeinset, imageEdgeInsets和titleEdgeInsets无效。我知道,负值会使边缘扩大,而正值会使边缘缩小,使边缘更接近中心。

我试着:

[button setTitleEdgeInsets:UIEdgeInsetsMake(0, -image.size.width, 0, 0)];
[button setImageEdgeInsets:UIEdgeInsetsMake(0, button.titleLabel.bounds.size.width, 0, 0)];

但这并没有正确显示它。我一直在调整这些值,但从-5到-10的左插入值似乎不会以预期的方式移动它。-10将文本一直向左移动,所以我期望-5将它从左侧移动一半,但事实并非如此。

嵌套背后的逻辑是什么?我不熟悉图像放置和相关术语。

我用这个SO问题作为参考,但我的价值观有些不对。 UIButton:如何使用imageEdgeInsets和titleEdgeInsets居中图像和文本?


当前回答

在swift 5.3和@ravron的启发下回答:

extension UIButton {
    /// Fits the image and text content with a given spacing
    /// - Parameters:
    ///   - spacing: Spacing between the Image and the text
    ///   - contentXInset: The spacing between the view to the left image and the right text to the view
    func setHorizontalMargins(imageTextSpacing: CGFloat, contentXInset: CGFloat = 0) {
        let imageTextSpacing = imageTextSpacing / 2
        
        contentEdgeInsets = UIEdgeInsets(top: 0, left: (imageTextSpacing + contentXInset), bottom: 0, right: (imageTextSpacing + contentXInset))
        imageEdgeInsets = UIEdgeInsets(top: 0, left: -imageTextSpacing, bottom: 0, right: imageTextSpacing)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: imageTextSpacing, bottom: 0, right: -imageTextSpacing)
    }
}

它增加了一个额外的水平边距,从视图到图像,从标签到视图

其他回答

我同意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;

@end

公元UIButton +位置。

@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];

当然,你可能会想要为此创建一个很好的方法,可能会添加第二个类别方法,这是留给读者的练习。

根据iOS 15及以上版本:

var config = UIButton.Configuration.plain() // depends on the button you want 
config.image = UIImage(named: "view_all") // set the image 
config.imagePadding = 5 // add the insets for the image 
button.configuration = config // assign the config to the button 

来源: https://developer.apple.com/documentation/uikit/uibutton/configuration

在接口生成器中。选择UIButton -> Attributes Inspector -> Edge=Title并修改边缘插入

接口构建器解决方案

事情正在发生变化,现在在XCode 13.1和iOS 15+的尺寸检查器不影响嵌入,而是在属性检查器下有填充和内容嵌入属性,这带来了预期的效果

为了向后兼容,需要在尺寸检查器下做插入,就像@ravron说的那样。在IB中需要做一些组合:

让我们假设图像和标题之间的距离为8pt 添加标题左插图为8页 这将从右侧剪辑文本,所以需要平衡添加-8pt右插入标题 然后按钮的右插入也需要调整增加右插入8pt 完成了!这个按钮在iOS 14和15中看起来很棒

在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)
}