I don't want to use a subview if I can avoid it. I want a UIButton
with a background image, text, and an image in it. Right now, when I do that, the image is on the left side of the text. The background image, text, and image all have different highlight states.
如果可以避免,我不想使用子视图。我想要一个带有背景图像,文本和图像的UIButton。现在,当我这样做的时候,图像就在文本的左边。背景图像、文本和图像都有不同的高光状态。
23 个解决方案
#1
138
Despite some of the suggested answers being very creative and extremely clever, the simplest solution is as follows:
尽管有些建议的答案非常有创意和非常聪明,但最简单的解决方案如下:
button.semanticContentAttribute = .forceRightToLeft
As simple as that.
那么简单。
EDIT: as the question has been asked a few times, this is iOS 9 +.
编辑:这个问题已经被问过几次了,这是ios9 +。
#2
457
Simplest solution:
最简单的解决方案:
iOS 10 & up, Swift:
iOS 10及以上,Swift:
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)
Before iOS 10, Swift/Obj-C:
在iOS 10之前,斯威夫特/ 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);
#3
158
Subclassing UIButton is completely unnecessary. Instead you can simply set a high left inset value for the image insets, and a small right inset for the title. Something like this:
子类化UIButton是完全不必要的。相反,您可以简单地为图像嵌套设置一个高左insetvalue,并为标题设置一个小的右嵌套值。是这样的:
button.imageEdgeInsets = UIEdgeInsetsMake(0., button.frame.size.width - (image.size.width + 15.), 0., 0.);
button.titleEdgeInsets = UIEdgeInsetsMake(0., 0., 0., image.size.width);
#4
134
UPDATED FOR XCODE 9 (Via Interface Builder)
更新XCODE 9(通过接口构建程序)
There's an easier way from the Interface Builder. Select the UIButton and select this option in the View Utilities:
接口构建器有一种更简单的方法。选择UIButton并在View实用工具中选择此选项:
That's it! Nice and simple!
就是这样!很简单!
OPTIONAL - 2nd step:
可选,第二步:
If you want to adjust the spacing between the image and the title you can change the Image Inset here:
如果你想调整图像和标题之间的间距,你可以改变图片的设置:
Hope that helps!
希望会有帮助!
#5
83
I'm giving Inspire48 the credit for this one. Based on his suggestion and looking at that other question I came up with this. Subclass UIButton and override these methods.
我为这一件感到自豪。根据他的建议,看看我提出的另一个问题。子类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
#6
71
Just update the insets when the title is changed. You need to compensate for the inset with an equal and opposite inset on the other side.
只要在标题更改时更新insets即可。你需要在另一边用一个相等的和相反的镶嵌来补偿镶嵌。
[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);
#7
59
All of these answers, as of January 2016, are unnecessary. In Interface Builder, set the View Semantic to Force Right-to-Left
, or if you prefer programmatic way, semanticContentAttribute = .forceRightToLeft
That will cause the image to appear on the right of your text.
从2016年1月开始,所有这些答案都是不必要的。在Interface Builder中,设置视图语义以强制从右到左,或者如果您喜欢编程方式,semanticContentAttribute = . forcerighttoleft将导致图像出现在文本的右边。
#8
#9
21
Update: Swift 3
迅速更新: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
}
}
Original answer for Swift 2:
Swift 2的原始回复:
A solution that handles all horizontal alignments, with a Swift implementation example. Just translate to Objective-C if needed.
处理所有水平对齐的解决方案,并提供一个快速实现示例。如果需要的话,翻译成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
}
}
Also worth noting that it handles quite well image & title insets.
同样值得注意的是,它能很好地处理图像和标题嵌套。
Inspired from jasongregori answer ;)
灵感来自jasongregori的回答;)
#10
6
Here is solution for UIButton
with center aligned content. This code make image right aligned and allows to use imageEdgeInsets
and titleEdgeInsets
for precious positioning.
这是带有中心对齐内容的UIButton的解决方案。此代码使图像对齐,并允许使用imageEdgeInsets和titleEdgeInsets进行宝贵的定位。
Subclass UIButton
with your custom class and add:
带有自定义类的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;
}
#11
4
If this need to be done in UIBarButtonItem, additional wrapping in view should be used
This will work
如果需要在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)
This won't work
这是行不通的
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)
#12
3
Building on Piotr Tomasik's elegant solution: if you want to have a bit of spacing between the button label and image as well, then include that in your edge insets as follows (copying my code here that works perfectly for me):
以Piotr Tomasik优雅的解决方案为基础:如果您想在按钮标签和图像之间也有一点间距,那么请将其包含在您的edge insets中,如下所示(复制我的代码,这对我来说非常有效):
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);
Thanks Piotr for your solution!
谢谢你的解决方案!
Erik
埃里克
#13
3
Being that the transform solution doesn't work in iOS 11 I decided to write a new approach.
由于转换解决方案在ios11中不起作用,我决定编写一种新的方法。
Adjusting the buttons semanticContentAttribute
gives us the image nicely to the right without having to relayout if the text changes. Because of this it's the ideal solution. However I still need RTL support. The fact that an app can not change it's layout direction in the same session resolves this issue easily.
通过调整按钮semanticContentAttribute,我们可以很好地将图像显示到右边,而不必在文本发生变化时放松。正因为如此,它是理想的解。但是我仍然需要RTL支持。应用不能在同一会话中改变布局方向,这一事实很容易解决这个问题。
With that said, it's pretty straight forward.
说到这里,这是非常直接的。
extension UIButton {
func alignImageRight() {
if UIApplication.shared.userInterfaceLayoutDirection == .leftToRight {
semanticContentAttribute = .forceRightToLeft
}
else {
semanticContentAttribute = .forceLeftToRight
}
}
}
#14
2
Took @Piotr's answer and made it into a Swift extension. Make sure to set the image and title before calling this, so that the button sizes properly.
接受了@Piotr的回答,并迅速扩展。在调用此按钮之前,请确保设置了图像和标题,以便按钮大小正确。
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)
}
}
}
}
#15
2
A swift option that does what you want without playing with any insets:
一个快速的选项,做你想要的,而不玩任何镶嵌:
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
}
}
#16
1
Subclassing and over-riding layoutSubviews is probably your best way to go.
子类化和重写layoutSubviews可能是最好的方法。
Referenced from: iPhone UIButton - image position
引用自:iPhone UIButton -图像位置
#17
1
Swift -Extend the UiButton and put these lines
Swift -扩展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);
}
#18
0
Solutions mentioned here stopped working, once I enabled Auto Layout. I had to come up with my own:
这里提到的解决方案停止了工作,一旦我启用了自动布局。我不得不提出我自己的观点:
Subclass UIButton and override layoutSubviews
method:
子类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;
}
Result:
结果:
#19
0
Swift 3:
斯威夫特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
}
#20
0
swift 3.0 Migration solution given by jasongregori
jasongregori提供的swift 3.0迁移解决方案
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
}
#21
0
How about Constraints? Unlike semanticContentAttribute, they don't change semantics. Something like this perhaps:
约束呢?与semanticContentAttribute不同,它们不会改变语义。也许是这样的:
button.rightAnchorconstraint(equalTo: button.rightAnchor).isActive = true
or in Objective-C:
或在objective - c中:
[button.imageView.rightAnchor constraintEqualToAnchor:button.rightAnchor].isActive = YES;
Caveats: Untested, iOS 9+
警告:未经测试,iOS 9 +
#22
-1
Thanks to Vitaliy Gozhenko
由于维塔利Gozhenko
I just want to add that you can add IB_DESIGNABLE before your button @interface and set your button class in storyborad. Then you can watch it layout in real time without app launch just at interface building stage
我只是想补充一点,您可以在按钮@interface之前添加IB_DESIGNABLE,并在storyborad中设置按钮类。然后你可以在界面构建阶段实时观看它的布局,无需app启动
#23
-4
The correct answer in Swift
正确答案用Swift
import UIKit
extension UIButton {
func imageRectForContentRect(contentRect:CGRect) -> CGRect {
var frame = self.imageRectForContentRect(contentRect)
frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(frame) - self.imageEdgeInsets.right + self.imageEdgeInsets.left
return frame
}
func titleRectForContentRect(contentRect:CGRect) -> CGRect {
var frame = self.titleRectForContentRect(contentRect)
frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(self.imageRectForContentRect(contentRect))
return frame
}
}
#1
138
Despite some of the suggested answers being very creative and extremely clever, the simplest solution is as follows:
尽管有些建议的答案非常有创意和非常聪明,但最简单的解决方案如下:
button.semanticContentAttribute = .forceRightToLeft
As simple as that.
那么简单。
EDIT: as the question has been asked a few times, this is iOS 9 +.
编辑:这个问题已经被问过几次了,这是ios9 +。
#2
457
Simplest solution:
最简单的解决方案:
iOS 10 & up, Swift:
iOS 10及以上,Swift:
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)
Before iOS 10, Swift/Obj-C:
在iOS 10之前,斯威夫特/ 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);
#3
158
Subclassing UIButton is completely unnecessary. Instead you can simply set a high left inset value for the image insets, and a small right inset for the title. Something like this:
子类化UIButton是完全不必要的。相反,您可以简单地为图像嵌套设置一个高左insetvalue,并为标题设置一个小的右嵌套值。是这样的:
button.imageEdgeInsets = UIEdgeInsetsMake(0., button.frame.size.width - (image.size.width + 15.), 0., 0.);
button.titleEdgeInsets = UIEdgeInsetsMake(0., 0., 0., image.size.width);
#4
134
UPDATED FOR XCODE 9 (Via Interface Builder)
更新XCODE 9(通过接口构建程序)
There's an easier way from the Interface Builder. Select the UIButton and select this option in the View Utilities:
接口构建器有一种更简单的方法。选择UIButton并在View实用工具中选择此选项:
That's it! Nice and simple!
就是这样!很简单!
OPTIONAL - 2nd step:
可选,第二步:
If you want to adjust the spacing between the image and the title you can change the Image Inset here:
如果你想调整图像和标题之间的间距,你可以改变图片的设置:
Hope that helps!
希望会有帮助!
#5
83
I'm giving Inspire48 the credit for this one. Based on his suggestion and looking at that other question I came up with this. Subclass UIButton and override these methods.
我为这一件感到自豪。根据他的建议,看看我提出的另一个问题。子类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
#6
71
Just update the insets when the title is changed. You need to compensate for the inset with an equal and opposite inset on the other side.
只要在标题更改时更新insets即可。你需要在另一边用一个相等的和相反的镶嵌来补偿镶嵌。
[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);
#7
59
All of these answers, as of January 2016, are unnecessary. In Interface Builder, set the View Semantic to Force Right-to-Left
, or if you prefer programmatic way, semanticContentAttribute = .forceRightToLeft
That will cause the image to appear on the right of your text.
从2016年1月开始,所有这些答案都是不必要的。在Interface Builder中,设置视图语义以强制从右到左,或者如果您喜欢编程方式,semanticContentAttribute = . forcerighttoleft将导致图像出现在文本的右边。
#8
48
In interface builder you can configure options Edge Insets for UIButton, separately each of three parts: content, image, title
在interface builder中,您可以为UIButton配置选项边缘Insets,分别包含三个部分:内容、图像、标题。
Xcode 8:
Xcode 8:
#9
21
Update: Swift 3
迅速更新: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
}
}
Original answer for Swift 2:
Swift 2的原始回复:
A solution that handles all horizontal alignments, with a Swift implementation example. Just translate to Objective-C if needed.
处理所有水平对齐的解决方案,并提供一个快速实现示例。如果需要的话,翻译成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
}
}
Also worth noting that it handles quite well image & title insets.
同样值得注意的是,它能很好地处理图像和标题嵌套。
Inspired from jasongregori answer ;)
灵感来自jasongregori的回答;)
#10
6
Here is solution for UIButton
with center aligned content. This code make image right aligned and allows to use imageEdgeInsets
and titleEdgeInsets
for precious positioning.
这是带有中心对齐内容的UIButton的解决方案。此代码使图像对齐,并允许使用imageEdgeInsets和titleEdgeInsets进行宝贵的定位。
Subclass UIButton
with your custom class and add:
带有自定义类的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;
}
#11
4
If this need to be done in UIBarButtonItem, additional wrapping in view should be used
This will work
如果需要在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)
This won't work
这是行不通的
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)
#12
3
Building on Piotr Tomasik's elegant solution: if you want to have a bit of spacing between the button label and image as well, then include that in your edge insets as follows (copying my code here that works perfectly for me):
以Piotr Tomasik优雅的解决方案为基础:如果您想在按钮标签和图像之间也有一点间距,那么请将其包含在您的edge insets中,如下所示(复制我的代码,这对我来说非常有效):
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);
Thanks Piotr for your solution!
谢谢你的解决方案!
Erik
埃里克
#13
3
Being that the transform solution doesn't work in iOS 11 I decided to write a new approach.
由于转换解决方案在ios11中不起作用,我决定编写一种新的方法。
Adjusting the buttons semanticContentAttribute
gives us the image nicely to the right without having to relayout if the text changes. Because of this it's the ideal solution. However I still need RTL support. The fact that an app can not change it's layout direction in the same session resolves this issue easily.
通过调整按钮semanticContentAttribute,我们可以很好地将图像显示到右边,而不必在文本发生变化时放松。正因为如此,它是理想的解。但是我仍然需要RTL支持。应用不能在同一会话中改变布局方向,这一事实很容易解决这个问题。
With that said, it's pretty straight forward.
说到这里,这是非常直接的。
extension UIButton {
func alignImageRight() {
if UIApplication.shared.userInterfaceLayoutDirection == .leftToRight {
semanticContentAttribute = .forceRightToLeft
}
else {
semanticContentAttribute = .forceLeftToRight
}
}
}
#14
2
Took @Piotr's answer and made it into a Swift extension. Make sure to set the image and title before calling this, so that the button sizes properly.
接受了@Piotr的回答,并迅速扩展。在调用此按钮之前,请确保设置了图像和标题,以便按钮大小正确。
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)
}
}
}
}
#15
2
A swift option that does what you want without playing with any insets:
一个快速的选项,做你想要的,而不玩任何镶嵌:
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
}
}
#16
1
Subclassing and over-riding layoutSubviews is probably your best way to go.
子类化和重写layoutSubviews可能是最好的方法。
Referenced from: iPhone UIButton - image position
引用自:iPhone UIButton -图像位置
#17
1
Swift -Extend the UiButton and put these lines
Swift -扩展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);
}
#18
0
Solutions mentioned here stopped working, once I enabled Auto Layout. I had to come up with my own:
这里提到的解决方案停止了工作,一旦我启用了自动布局。我不得不提出我自己的观点:
Subclass UIButton and override layoutSubviews
method:
子类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;
}
Result:
结果:
#19
0
Swift 3:
斯威夫特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
}
#20
0
swift 3.0 Migration solution given by jasongregori
jasongregori提供的swift 3.0迁移解决方案
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
}
#21
0
How about Constraints? Unlike semanticContentAttribute, they don't change semantics. Something like this perhaps:
约束呢?与semanticContentAttribute不同,它们不会改变语义。也许是这样的:
button.rightAnchorconstraint(equalTo: button.rightAnchor).isActive = true
or in Objective-C:
或在objective - c中:
[button.imageView.rightAnchor constraintEqualToAnchor:button.rightAnchor].isActive = YES;
Caveats: Untested, iOS 9+
警告:未经测试,iOS 9 +
#22
-1
Thanks to Vitaliy Gozhenko
由于维塔利Gozhenko
I just want to add that you can add IB_DESIGNABLE before your button @interface and set your button class in storyborad. Then you can watch it layout in real time without app launch just at interface building stage
我只是想补充一点,您可以在按钮@interface之前添加IB_DESIGNABLE,并在storyborad中设置按钮类。然后你可以在界面构建阶段实时观看它的布局,无需app启动
#23
-4
The correct answer in Swift
正确答案用Swift
import UIKit
extension UIButton {
func imageRectForContentRect(contentRect:CGRect) -> CGRect {
var frame = self.imageRectForContentRect(contentRect)
frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(frame) - self.imageEdgeInsets.right + self.imageEdgeInsets.left
return frame
}
func titleRectForContentRect(contentRect:CGRect) -> CGRect {
var frame = self.titleRectForContentRect(contentRect)
frame.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(self.imageRectForContentRect(contentRect))
return frame
}
}