我想在两行文本的左侧放置一个图标,使图像和文本开头之间有大约 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)];
但这不能正确显示。我一直在调整这些值,但是从左侧插入值的 -5 到 -10 似乎并没有以预期的方式移动它。 -10 会将文本一直向左移动,所以我希望 -5 将它从左侧移动到一半,但事实并非如此。
插图背后的逻辑是什么?我不熟悉图像放置和相关术语。
我用这个 SO question 作为参考,但我的价值观有些不对。 UIButton: how to center an image and a text using imageEdgeInsets and titleEdgeInsets?
我参加这个聚会有点晚了,但我想我有一些有用的东西要补充。
Kekoa 的回答很好,但是,正如 RonLugge 所提到的,它可以使按钮不再尊重 sizeToFit
,或者更重要的是,可以导致按钮在固有大小时剪切其内容。哎呀!
不过,首先,
简要说明我认为 imageEdgeInsets
和 titleEdgeInsets
的工作原理:
docs for imageEdgeInsets
部分内容如下:
使用此属性调整按钮图像的有效绘图矩形的大小和重新定位。您可以为四个插图(上、左、下、右)中的每一个指定不同的值。正值会缩小或插入该边缘 - 将其移近按钮的中心。负值扩展或开始该边缘。
我相信这个文档是在想象按钮没有标题的情况下编写的,只有一个图像。以这种方式思考更有意义,并且表现出 UIEdgeInsets
通常的行为。基本上,图像的框架(或标题,带有 titleEdgeInsets
)向内移动以获取正插图,向外移动以获取负插图。
好的,那又怎样?
我快到那里了!这是您默认设置的内容,设置图像和标题(按钮边框为绿色只是为了显示它的位置):
https://i.stack.imgur.com/9BhvR.png
当您想要图像和标题之间的间距时,不会导致任何一个被压碎,您需要设置四个不同的插图,每个图像和标题两个。那是因为您不想更改这些元素框架的大小,而只想更改它们的位置。当您开始以这种方式思考时,对 Kekoa 的优秀类别所需的更改变得清晰:
@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
但是等等,你说,当我这样做时,我得到了这个:
https://i.stack.imgur.com/GmVAb.png
哦耶!我忘了,the docs 警告过我这件事。他们说,部分:
此属性仅用于在布局期间定位图像。该按钮不使用此属性来确定intrinsicContentSize 和sizeThatFits:。
但有一个属性可以提供帮助,那就是 contentEdgeInsets
。 The docs 部分原因是:
该按钮使用此属性来确定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
你得到了什么?
https://i.stack.imgur.com/uTUPc.png
对我来说似乎是赢家。
在 Swift 中工作并且根本不想做任何思考?这是 Swift 中扩展的最终版本:
extension UIButton {
func centerTextAndImage(spacing: CGFloat) {
let insetAmount = spacing / 2
let isRTL = UIView.userInterfaceLayoutDirection(for: semanticContentAttribute) == .rightToLeft
if isRTL {
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)
} else {
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)
}
}
}
我同意 imageEdgeInsets
和 titleEdgeInsets
上的文档应该更好,但我想出了如何在不诉诸试验和错误的情况下获得正确的定位。
总体思路在 this question,但前提是您希望文本和图像都居中。我们不希望图像和文本单独居中,我们希望图像和文本作为一个实体一起居中。这实际上是 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+Position.h
@interface UIButton(ImageTitleCentering)
-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing;
@end
UIButton+位置.m
@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];
当然,您可能希望为此创建一个不错的方法,可能会添加第二类方法,这留给读者作为练习。
[UIButton setTitleColor:forState:]
甚至 [UIButton setTitle:forState:]
,则定位不应改变。
另外,如果你想做类似的东西
https://i.stack.imgur.com/qjino.png
你需要
1.设置按钮的水平和垂直对齐方式
https://i.stack.imgur.com/mMHjv.png
找到所有需要的值并设置 UIImageEdgeInsets CGSize 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; //图文垂直间距 [button setImageEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 - offsetBetweenImageAndText, (buttonSize.width - buttonImageSize.width) / 2, 0,0) ]; [按钮 setTitleEdgeInsets:UIEdgeInsetsMake((buttonSize.height - (titleSize.height + buttonImageSize.height)) / 2 + buttonImageSize.height + offsetBetweenImageAndText, titleSize.width + [button imageEdgeInsets].left > buttonSize.width ? -buttonImage.size.宽度 + (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)
}
}
}
}
在界面生成器中。选择 UIButton -> Attributes Inspector -> Edge=Title 并修改边缘插图
使用这个可以避免很多麻烦——
myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
myButton.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
这会将您的所有内容自动对齐到左侧(或您想要的任何位置)
斯威夫特 3:
myButton.contentHorizontalAlignment = UIControlContentHorizontalAlignment.left;
myButton.contentVerticalAlignment = UIControlContentVerticalAlignment.center;
在 Xcode 8.0 中,您只需更改尺寸检查器中的 insets
即可。
选择 UIButton -> Attributes Inspector -> 转到大小检查器并修改内容、图像和标题插图。
https://i.stack.imgur.com/9pdLu.png
如果您想更改右侧的图像,您只需在 Attribute inspector 中将语义属性更改为 Force Right-to-left
即可。
https://i.stack.imgur.com/22gz8.png
我参加这个聚会也有点晚了,但我想我有一些有用的东西要补充:o)。
我创建了一个 UIButton
子类,其目的是能够选择按钮图像的布局位置,垂直或水平。
https://i.stack.imgur.com/PEtYh.png
这里是关于如何用我的类创建这些按钮的详细信息:
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 更改按钮类 使用“center”、“top”、“bottom”、“left”或“right”调整可布局属性
这里的代码 (gist) :
@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()
}
}
error: IB Designables: Failed to update auto layout status: The agent crashed
、gist.github.com/nebiros/ecf69ff9cb90568edde071386c6c4ddb 破坏 IB
error: IB Designables: Failed to update auto layout status: The agent crashed
,因为 init(frame: CGRect)
没有被覆盖,另外,我添加了 @available
注释......,如果你愿意,你可以 diff -Naur
,;-)
斯威夫特 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)
我将根据更新后的 Xcode 13
更新答案。
用于图像和文本对齐。我们不必使用任何扩展或一行代码。 Xcode 在属性选项卡中提供预定义属性,如下图所示。
展示位置属性有 4 个图像属性 - Top, Bottom, Leading, and Trailing
https://i.stack.imgur.com/8SX2L.png
Riley Avron 对帐户区域设置更改的一个小补充:
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)
}
}
界面构建器解决方案
情况正在发生变化,现在使用 XCode 13.1 并且对于 iOS 15+,Size inspector
不会影响插入,而是在 Attribute inspector
下有 Padding
和 Content insets
属性,它们会带来预期的效果
https://i.stack.imgur.com/g2KZj.png
正如@ravron 所说,为了向后兼容,需要在 Size inspector
下插入。在IB中需要做一些组合:
假设我们希望图像和标题之间的距离为 8pt 添加标题左插图为 8 pt 这将从右侧剪切文本,因此需要与为标题添加 -8pt 右插图平衡,然后按钮的右插图也需要调整增加右插图 8pt 完成!该按钮在 iOS 14 和 15 上看起来很棒
https://i.stack.imgur.com/uo4IM.png
在 iOS 15+
中,您可以使用 UIButton.Configuration:
var configuration = button.configuration
configuration?.imagePadding = 16
configuration?.titlePadding = 10
button.configuration = configuration
'imageEdgeInsets' was deprecated in iOS 15.0: This property is ignored when using UIButtonConfiguration
我在下面写代码。它在产品版本中运行良好。支持 Swift 4.2 +
extension UIButton{
enum ImageTitleRelativeLocation {
case imageUpTitleDown
case imageDownTitleUp
case imageLeftTitleRight
case imageRightTitleLeft
}
func centerContentRelativeLocation(_ relativeLocation:
ImageTitleRelativeLocation,
spacing: CGFloat = 0) {
assert(contentVerticalAlignment == .center,
"only works with contentVerticalAlignment = .center !!!")
guard (title(for: .normal) != nil) || (attributedTitle(for: .normal) != nil) else {
assert(false, "TITLE IS NIL! SET TITTLE FIRST!")
return
}
guard let imageSize = self.currentImage?.size else {
assert(false, "IMGAGE IS NIL! SET IMAGE FIRST!!!")
return
}
guard let titleSize = titleLabel?
.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) else {
assert(false, "TITLELABEL IS NIL!")
return
}
let horizontalResistent: CGFloat
// extend contenArea in case of title is shrink
if frame.width < titleSize.width + imageSize.width {
horizontalResistent = titleSize.width + imageSize.width - frame.width
print("horizontalResistent", horizontalResistent)
} else {
horizontalResistent = 0
}
var adjustImageEdgeInsets: UIEdgeInsets = .zero
var adjustTitleEdgeInsets: UIEdgeInsets = .zero
var adjustContentEdgeInsets: UIEdgeInsets = .zero
let verticalImageAbsOffset = abs((titleSize.height + spacing) / 2)
let verticalTitleAbsOffset = abs((imageSize.height + spacing) / 2)
switch relativeLocation {
case .imageUpTitleDown:
adjustImageEdgeInsets.top = -verticalImageAbsOffset
adjustImageEdgeInsets.bottom = verticalImageAbsOffset
adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2
adjustTitleEdgeInsets.top = verticalTitleAbsOffset
adjustTitleEdgeInsets.bottom = -verticalTitleAbsOffset
adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2
adjustContentEdgeInsets.top = spacing
adjustContentEdgeInsets.bottom = spacing
adjustContentEdgeInsets.left = -horizontalResistent
adjustContentEdgeInsets.right = -horizontalResistent
case .imageDownTitleUp:
adjustImageEdgeInsets.top = verticalImageAbsOffset
adjustImageEdgeInsets.bottom = -verticalImageAbsOffset
adjustImageEdgeInsets.left = titleSize.width / 2 + horizontalResistent / 2
adjustImageEdgeInsets.right = -titleSize.width / 2 - horizontalResistent / 2
adjustTitleEdgeInsets.top = -verticalTitleAbsOffset
adjustTitleEdgeInsets.bottom = verticalTitleAbsOffset
adjustTitleEdgeInsets.left = -imageSize.width / 2 + horizontalResistent / 2
adjustTitleEdgeInsets.right = imageSize.width / 2 - horizontalResistent / 2
adjustContentEdgeInsets.top = spacing
adjustContentEdgeInsets.bottom = spacing
adjustContentEdgeInsets.left = -horizontalResistent
adjustContentEdgeInsets.right = -horizontalResistent
case .imageLeftTitleRight:
adjustImageEdgeInsets.left = -spacing / 2
adjustImageEdgeInsets.right = spacing / 2
adjustTitleEdgeInsets.left = spacing / 2
adjustTitleEdgeInsets.right = -spacing / 2
adjustContentEdgeInsets.left = spacing
adjustContentEdgeInsets.right = spacing
case .imageRightTitleLeft:
adjustImageEdgeInsets.left = titleSize.width + spacing / 2
adjustImageEdgeInsets.right = -titleSize.width - spacing / 2
adjustTitleEdgeInsets.left = -imageSize.width - spacing / 2
adjustTitleEdgeInsets.right = imageSize.width + spacing / 2
adjustContentEdgeInsets.left = spacing
adjustContentEdgeInsets.right = spacing
}
imageEdgeInsets = adjustImageEdgeInsets
titleEdgeInsets = adjustTitleEdgeInsets
contentEdgeInsets = adjustContentEdgeInsets
setNeedsLayout()
}
}
这是一个如何使用 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 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)
}
}
它增加了从 View 到 Image 以及从 Label 到 View 的额外水平边距
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)
}
我的垂直居中方法:
extension UIButton {
/// Layout image and title with vertical centering.
/// - Parameters:
/// - size: The button size.
/// - imageTopOffset: Top offset for image.
/// - spacing: Distance between image and title.
func verticalAlignmentByCenter(size: CGSize, imageTopOffset: CGFloat, spacing: CGFloat) {
let contentRect = contentRect(forBounds: CGRect(origin: .zero, size: size))
let imageRect = imageRect(forContentRect: contentRect)
let titleRect = titleRect(forContentRect: contentRect)
let imageTop = imageTopOffset - imageRect.origin.y
let imageLeft = contentRect.width/2 - imageRect.width/2
imageEdgeInsets = UIEdgeInsets(top: imageTop, left: imageLeft, bottom: 0, right: 0)
let titleTop = imageTopOffset + spacing + imageRect.height - titleRect.origin.y
let titleLeft = titleRect.origin.x - contentRect.width/2 - titleRect.width/2
titleEdgeInsets = UIEdgeInsets(top: titleTop, left: titleLeft, bottom: 0, right: 0)
}
}
@ravron 在提供这个答案方面做得非常出色。
就我而言,我不仅需要在图像和标题之间添加水平宽度,还需要在按钮的“前导”和“尾随”中添加水平空间。
因此,我使用了内部图像和标签的 intrinsicContentSize:
接收视图的自然大小,仅考虑视图本身的属性。
| |
|[LEADING SPACE] [Image] [SPACE BETWEEN IMAGE AND TITLE] Add To [TRAILING SPACE]|
| Favorites |
let leadingTrailingSpace = 10
let horizontalWidthBetweenImageAndTitle = 4
let insetAmount = horizontalWidthBetweenImageAndTitle / CGFloat(2)
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: -CGFloat(insetAmount), bottom: 0, right: insetAmount);
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: CGFloat(insetAmount), bottom: 0, right: -insetAmount);
button.contentEdgeInsets = UIEdgeInsets(top: 0, left: CGFloat(insetAmount), bottom: 0, right: insetAmount);
let buttonWidth =
(button.titleLabel?.intrinsicContentSize.width ?? 0) +
(button.imageView?.intrinsicContentSize.width ?? 0)
+ insetAmount
+ leadingTrailingSpace
button.widthAnchor.constraint(equalToConstant: buttonWidth).isActive = true
swift 4.2 版本的解决方案如下:
let spacing: CGFloat = 10 // the amount of spacing to appear between image and title
self.button?.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: spacing)
self.button?.titleEdgeInsets = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: 0)
intrinsicContentSize
不正确的问题,这在这些自动布局的日子里非常重要,因为原始答案被接受了。spacing
添加到 self.contentEdgeInsets 的四个值中,如下所示:self.contentEdgeInsets = UIEdgeInsetsMake(spacing, spacing + insetAmount, spacing, spacing + insetAmount);
button.imageView?.contentMode = .scaleAspectFit
。我为此制作了一个小测试应用程序,您可以使用 github.com/tomas789/UIButtonEdgeInsets