iOS 按钮默认布局方式是左图右文,但是有时候我们需要左文右图的布局。
下面总结了几种方案。要结论的同学直接看方案五。
方案一
直接使用UIButton,设置其semanticContentAttribute为UISemanticContentAttributeForceRightToLeft。即布局改为从右至左。这个属性原本用于阿拉伯文这种从右向左写的语言。
button.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
优点:
- 方便快捷。
缺点:
- 需要iOS 9.0及以上。
- 需要纯代码,我在xib测试无效。
方案二
直接使用UIButton,设置其titleEdgeInsets或imageEdgeInsets。也就是让图片右移文本的宽度,文本左移图片的宽度。
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width, 0, button.imageView.frame.size.width);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, -button.titleLabel.frame.size.width);
优点:
- 代码与xib均可以使用。
缺点:
- 文本改变后需要重新计算。
- 如果使用自动布局,需要frame确定后才能计算,或者自己用 boundingRectWithSize:options:attributes:context: 计算出title的size。
方案三
方案三是方案二的变种,即派生一个UIButton的子类,来实现titleEdgeInsets与imageEdgeInsets的自动更新。
- (void)layoutSubviews {
[super layoutSubviews];
[self updateEdgeInsets];
}
- (void)updateEdgeInsets {
self.titleEdgeInsets = UIEdgeInsetsMake(0, -self.imageView.frame.size.width, 0, self.imageView.frame.size.width);
self.imageEdgeInsets = UIEdgeInsetsMake(0, self.titleLabel.frame.size.width, 0, -self.titleLabel.frame.size.width);
}
之所以在layoutSubviews中调用更新方法,是因为无论是自动布局还是更改内容都会触发这个方法,并且子视图的frame都已经确定。
优点:
- 代码与xib均可以使用,且不像方案二那样需要手动重新计算。
缺点:
- 碰到那种需要调整文本与图片间距的需求,就不能使用titleEdgeInsets与imageEdgeInsets属性了,需要额外添加属性来处理。
方案四
派生一个UIButton的子类,通过重载 titleRectForContentRect: 和 imageRectForContentRect: 来分别指定文本和图片的显示区域。
@interface FCReverseButton ()
@property (nonatomic, assign) CGRect originalImageRect; // 原图片区域
@property (nonatomic, assign) CGRect originalTitleRect; // 原文本区域
@end
@implementation FCReverseButton
- (CGRect)titleRectForContentRect:(CGRect)contentRect {
if (CGRectIsEmpty(self.originalTitleRect)) {
self.originalTitleRect = [super titleRectForContentRect:contentRect];
}
if (CGRectIsEmpty(self.originalTitleRect) || CGRectIsEmpty(self.originalImageRect)) {
return self.originalTitleRect;
} else {
CGFloat height = MAX(CGRectGetHeight(self.originalTitleRect), CGRectGetHeight(self.originalImageRect));
return CGRectMake(contentRect.origin.x + self.titleEdgeInsets.right, contentRect.origin.y + (height - CGRectGetHeight(self.originalTitleRect)) / 2, CGRectGetWidth(self.originalTitleRect), CGRectGetHeight(self.originalTitleRect));
}
}
- (CGRect)imageRectForContentRect:(CGRect)contentRect {
if (CGRectIsEmpty(self.originalImageRect)) {
self.originalImageRect = [super imageRectForContentRect:contentRect];
}
if (CGRectIsEmpty(self.originalTitleRect) || CGRectIsEmpty(self.originalImageRect)) {
return self.originalImageRect;
} else {
CGFloat height = MAX(CGRectGetHeight(self.originalTitleRect), CGRectGetHeight(self.originalImageRect));
return CGRectMake(contentRect.origin.x + CGRectGetWidth(self.originalTitleRect) + self.imageEdgeInsets.right, contentRect.origin.y + (height - CGRectGetHeight(self.originalImageRect)) / 2, CGRectGetWidth(self.originalImageRect), CGRectGetHeight(self.originalImageRect));
}
}
@end
优点:
- 可以兼容titleEdgeInsets与imageEdgeInsets属性,内容变化时会自动重新计算。
缺点
- 不能在xib中使用。(在xib中使用时 [super titleRectForContentRect:contentRect] 返回的rect的size是zero,很奇怪。)
方案五
在坐标系的层面翻转按钮的X轴,再分别将titleLabel和imageView的X轴翻转回来。可以直接对UIButton进行操作,也可以派生一个子类。建议派生一个子类。
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self horizontalReverse];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self horizontalReverse];
}
return self;
}
// 水平翻转
- (void)horizontalReverse {
self.transform = CGAffineTransformMakeScale(-1, 1);
self.imageView.transform = CGAffineTransformMakeScale(-1, 1);
self.titleLabel.transform = CGAffineTransformMakeScale(-1, 1);
}
优点
- 不多说了,悄悄告诉你我用的这个方案。
缺点
- 在通过titleEdgeInsets与imageEdgeInsets来调整间距的时候,需要反着来,即左移的结果是右移,右移的结果是左移。
参考:
How do I put the image on the right side of the text in a UIButton?
网友评论