UIButton的图文混排

逻辑思路:
button设置图片和文字后后再设置titleEdgeInsets属性和 imageEdgeInsets属性实现button的上图下文,上文下图,左图右文,右图左文的重新排列(自由设置图文间距)

UIEdgInsets官方解释:

Edge inset values are applied to a rectangle to shrink or expand the area represented by that rectangle. Typically, edge insets are used during view layout to modify the view’s frame. Positive values cause the frame to be inset (or shrunk) by the specified amount. Negative values cause the frame to be outset (or expanded) by the specified amount.

说人话就是:UIEdgInsets是用来扩大或者缩小控件的矩形区域,通常在视图布局期间修改视图框架,正值缩小视图框架, 负值扩大视图框架

左文右图

默认情况下,button的image和label是紧贴着居中的,那如果想要image在右边,label在左边应该怎么办呢?

答案就是:

self.imageEdgeInsets = UIEdgeInsetsMake(0, labelWidth + spacing/2, 0, -(labelWidth + spacing/2));
self.titleEdgeInsets = UIEdgeInsetsMake(0, -(imageHeight + spacing/2), 0, imageHeight + spacing/2);

其实就是这一句:This property is used only for positioning the image during layout
其实titleEdgeInsets属性和 imageEdgeInsets属性只是在画这个button出来的时候用来调整image和label位置的属性,并不影响button本身的大小。
UIEdgeInsetsMake(CGFloat top, CGFloat left, CGFloat bottom, CGFloat right)

它们只是image和button相较于原来位置的偏移量,那什么是原来的位置呢?就是没有设置edgeInset时候的位置了
imageEdgeInsets,titleEdgeInsets默认为0,spacing为图片文字间隔
如果image在右边,label在左边,二者的上下的偏移量相较于原来的位置没有改变,为0。

左右偏移量的计算:


左文右图

偏移的距离
imageOffset = | 初始X - 修改后X | = showLabW + spacing/2

备注

  • showLabW 代表titleLable在button内部的初始(original)显示宽度,当button的宽度不够时,titleLable会被压缩,此时宽度小于完全显示时的真实宽度trueLabW,参见后图。
  • 宽度不够的情况下titleLable会优先被压缩,imageView的宽度一般是正确的
  • 此处认为showLabH = trueLabH,没有考虑label多行显示的情形

正负值判断
假设只改变image的一条边距的偏移量,如果image缩小,偏移量为正,反之为负;
self.imageEdgeInsets = UIEdgeInsetsMake(0, showLabW + spacing/2, 0, -(showLabW + spacing/2));
label计算过程同理:
self.titleEdgeInsets = UIEdgeInsetsMake(0, -(imgW + spacing/2), 0, imgW + spacing/2);

上图下文

上图下文和上文下图计算方式一样(实际四种模式计算方式都一样),下面就以上图下文的计算为例。

上图下文

偏移距离
imageOffsetX = labW/2
imageOffsetY = labH/2 + spacing/2
labelOffsetX = imgW/2
labelOffsetY = imgH/2 + spacing/2

提示:
在实际应用中发现这种情况在button的宽度大于 ImageWidth+lableWidth是没有问题的,但是当小于的时候就不行了

button宽度足够


button宽度不够

为什么呢

比较上两张图发现lable中心点的位置是正确的,只是左右的边缘的偏移量(决定宽度)没有计算好,ImageView的中心点位置是错误的,大小是正确的。

CGFloat labelWidth = [self.titleLabel.text sizeWithAttributes:@{NSFontAttributeName:self.titleLabel.font}].width;
CGFloat labelHeight = [self.titleLabel.text sizeWithAttributes:@{NSFontAttributeName:self.titleLabel.font}].height;

这里计算的是lable的真实宽度,而button宽度不够时,label会被压缩的显示宽度是小于真实宽度的,我们在重新排列的时候计算Image中心点的位置应该用lable的显示宽度而不是真实宽度。
同样,计算lable左右边缘的偏移量也应该把lable的真实宽度考虑进去,详情见下图
X1,X2分别为lable的左右边缘x值。


上图下文

偏移距离

//image中心移动的x距离
CGFloat imageOffsetX = showLabW /2 ;
//image中心移动的y距离
CGFloat imageOffsetY = showLabH / 2 + spacing / 2;
//label左边缘移动的x距离
CGFloat labelOffsetX1 = imgW/2 - showLabW/2 + trueLabW/2;
//label右边缘移动的x距离
CGFloat labelOffsetX2 = imgW/2 + showLabW/2 - trueLabW/2;
//label中心移动的y距离
CGFloat labelOffsetY = imgH / 2 + spacing / 2;

注意:当button宽度够大时 showLabW = trueLabW此时labelOffsetX1 = labelOffsetX2 = imgW/2和上面情况一样一样的,也就是说这种计算方法也适用于上面的情景

正负值判断后结果为
self.imageEdgeInsets = UIEdgeInsetsMake(-imageOffsetY, imageOffsetX, imageOffsetY, -imageOffsetX);
self.titleEdgeInsets = UIEdgeInsetsMake(labelOffsetY, -labelOffsetX1, -labelOffsetY, labelOffsetX2);

上文下图

上文下图

偏移距离

//image中心移动的x距离
CGFloat imageOffsetX = showLabW /2 ;
//image中心移动的y距离
CGFloat imageOffsetY = showLabH / 2 + spacing / 2;
//label左边缘移动的x距离
CGFloat labelOffsetX1 = imgW/2 - showLabW/2 + trueLabW/2;
//label右边缘移动的x距离
CGFloat labelOffsetX2 = imgW/2 + showLabW/2 - trueLabW/2;
//label中心移动的y距离
CGFloat labelOffsetY = imgH / 2 + spacing / 2;

结果竟然跟上面一样,说明这种方法有点笨了,肯定有不同的理解方法与解决方案,在这里就抛砖引玉了,欢迎交流。

正负值判断后结果为
self.imageEdgeInsets = UIEdgeInsetsMake(imageOffsetY, imageOffsetX, -imageOffsetY, -imageOffsetX);
self.titleEdgeInsets = UIEdgeInsetsMake(-labelOffsetY, -labelOffsetX1, labelOffsetY, labelOffsetX2);

实际应用

这些处理是专门对Button进行的,最好的应用场景当然是写到Button的Category 里面,方便外面调用。

#import <UIKit/UIKit.h>

typedef NS_ENUM(NSInteger, ZCImagePosition) {
    ZCImagePositionLeft,   //图片在左,标题在右,默认风格
    ZCImagePositionRight,  //图片在右,标题在左
    ZCImagePositionTop,    //图片在上,标题在下
    ZCImagePositionBottom  //图片在下,标题在上
};


NS_ASSUME_NONNULL_BEGIN

/**
 默认情况下,imageEdgeInsets和titleEdgeInsets都是0。先不考虑height,
 
 if (button.width小于imageView上image的width){图像会被压缩,文字不显示}
 
 if (button.width < imageView.width + label.width){图像正常显示,文字显示不全,这种情况下竖直排列时,要特别注意,已解决}
 //www.greatytc.com/p/f521505beed9
 if (button.width >= imageView.width + label.width){图像和文字都居中显示,imageView在左,label在右,中间没有空隙}
 */
@interface UIButton (EdgeInsets)


/**
 *  利用UIButton的titleEdgeInsets和imageEdgeInsets来实现文字和图片的自由排列
 *  注意:这个方法需要在设置图片和文字之后才可以调用,且button的大小要大于 图片大小spacing
 *
 *  @param spacing 图片和文字的间隔
 */
- (void)setImagePosition:(ZCImagePosition)postion spacing:(CGFloat)spacing;

/**
 *  利用UIButton的titleEdgeInsets和imageEdgeInsets来实现文字和图片的自由排列
 *  注意:这个方法需要在设置图片和文字之后才可以调用,且button的大小要大于 图片大小+文字大小+spacing
 *
 *  @param margin 图片、文字离button边框的距离
 */
- (void)setImagePosition:(ZCImagePosition)postion WithMargin:(CGFloat )margin;

@end

NS_ASSUME_NONNULL_END

#import "UIButton+EdgeInsets.h"

@implementation UIButton (EdgeInsets)

#pragma mark -- 按钮上图文混排
- (void)setImagePosition:(ZCImagePosition)postion spacing:(CGFloat)spacing {
    self.titleEdgeInsets = self.imageEdgeInsets = UIEdgeInsetsZero;
    
    CGFloat imgW = self.imageView.image.size.width;
    CGFloat imgH = self.imageView.image.size.height;
    CGSize showLabSize = self.titleLabel.bounds.size;
    CGFloat showLabW = showLabSize.width;
    CGFloat showLabH = showLabSize.height;
    
    CGSize trueSize = [self.titleLabel sizeThatFits:CGSizeMake(MAXFLOAT, MAXFLOAT)];
    CGFloat trueLabW = trueSize.width;

    //image中心移动的x距离
    CGFloat imageOffsetX = showLabW/2 ;
    //image中心移动的y距离
    CGFloat imageOffsetY = showLabH/2 + spacing/2;
    //label左边缘移动的x距离
    CGFloat labelOffsetX1 = imgW/2 - showLabW/2 + trueLabW/2;
    //label右边缘移动的x距离
    CGFloat labelOffsetX2 = fabs(imgW/2 + showLabW/2 - trueLabW/2);
    //label中心移动的y距离
    CGFloat labelOffsetY = imgH/2 + spacing/2;
    
    switch (postion) {
        case ZCImagePositionLeft:
            self.imageEdgeInsets = UIEdgeInsetsMake(0, -spacing/2, 0, spacing/2);
            self.titleEdgeInsets = UIEdgeInsetsMake(0, spacing/2, 0, -spacing/2);
            break;
            
        case ZCImagePositionRight:
            self.imageEdgeInsets = UIEdgeInsetsMake(0, showLabW + spacing/2, 0, -(showLabW + spacing/2));
            self.titleEdgeInsets = UIEdgeInsetsMake(0, -(imgW + spacing/2), 0, imgW + spacing/2);
            break;
            
        case ZCImagePositionTop:
            self.imageEdgeInsets = UIEdgeInsetsMake(-imageOffsetY, imageOffsetX, imageOffsetY, -imageOffsetX);
            self.titleEdgeInsets = UIEdgeInsetsMake(labelOffsetY, -labelOffsetX1, -labelOffsetY, labelOffsetX2);
            break;
            
        case ZCImagePositionBottom:
            self.imageEdgeInsets = UIEdgeInsetsMake(imageOffsetY, imageOffsetX, -imageOffsetY, -imageOffsetX);
            self.titleEdgeInsets = UIEdgeInsetsMake(-labelOffsetY, -labelOffsetX1, labelOffsetY, labelOffsetX2);
            break;
            
        default:
            break;
    }
    
}

/**根据图文距边框的距离调整图文间距*/
- (void)setImagePosition:(ZCImagePosition)postion WithMargin:(CGFloat )margin{
    if (postion == ZCImagePositionLeft || postion == ZCImagePositionRight) {
        CGFloat imageWith = self.imageView.image.size.width;
        CGFloat labelWidth = [self.titleLabel sizeThatFits:CGSizeMake(MAXFLOAT, MAXFLOAT)].width;
        CGFloat spacing = self.bounds.size.width - imageWith - labelWidth - 2*margin;
        
        [self setImagePosition:postion spacing:spacing];
    }else {
        CGFloat imageHeight = self.imageView.image.size.height;
        CGFloat labelHeight = [self.titleLabel sizeThatFits:CGSizeMake(MAXFLOAT, MAXFLOAT)].height;
        CGFloat spacing = self.bounds.size.height - imageHeight - labelHeight - 2*margin;
        
        [self setImagePosition:postion spacing:spacing];
    }
}


@end

注意事项

  • 在设置图片和文字之后,并且button的size不能为0调用本方法才能计算出正确的偏移量。
  • 每次修改button的图片或文字后都要再调用一下该方法,否则排版可能会混乱。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容