iOS的UIButton是一个很是常见并且经常使用的控件,咱们通常用他来实现某个功能的提交以及选择操做。咱们能够创建只有文字的Button,也能够创建只有图片的Button,具体的需求要看界面设计的具体状况。有时候咱们但愿应用的界面元素是丰富多彩的,有时候但愿创建一个图文结合的控件来响应用户的手势操做,所以创建一个即有图片也有文字的按钮来实现功能,这个只须要分别调用UIButton的setTitle:forState:
和setImage:forSate:
两个方法就能够实现具备图片和文字功能的按钮。可是系统默认的图文结合的按钮布局是:图片在左边而文字在右边,并且总体水平和垂直居中。好比下面这个图文按钮:
可是有的时候咱们又但愿图片在右边而文字在左边;或者图片在上边而文字在下面;或者图片在按钮的中间而文字在图片的下面等等,但咱们又不想放弃使用按钮这个控件,这时候怎么办? 事件老是能找到解决方法的, 有的人会先创建一个按钮控件铺在下面,而在上面分别覆盖一个UIImageView和UILabel来实现;而有的人则干脆在UIButton上创建一个UIImageView和UILabel两个子视图;而有的人则不会用UIButton来实现图文结合的功能。 前面说的几个方法看起来有效,也确实会解决问题,但缺点是代码量会增长,并且必须同时管理UIButton, UIImageView, UILabel这三个总体,若是哪天产品还但愿有一个按钮按下高亮或者按下阴影效果时,你可能又要从新编写代码实现需求了。
那么咱们是否要放弃UIButton呢?答案是否认的,其实UIButton自己是能够支持各类图文结合的,既然UIButton上能同时显示图片和文字,那就能够确定的说UIButton里面自己必定有一个UIImageView和UILabel8子视图。UIButton*自己就是一个复合控件,他分别提供了两个属性:git
@property(nonatomic,readonly,retain)UILabel *titleLabel NS_AVAILABLE_IOS(3_0); @property(nonatomic,readonly,retain)UIImageView *imageView NS_AVAILABLE_IOS(3_0);
须要注意的是这两个属性必需要调用完setTitle:forSate:
和setImage:forSate:
后才能获取到,不然有可能会返回nil。 其中的 titleLabel是用来保存文字的而imageView是用来保存图片的。那既然UIButton自己就带有一个图片控件和文本控件,那是否是咱们只要分别经过调整子控件的frame值就能实现咱们想要的图片文字的任何布局呢? 答案是否认的。实验证实经过设置titleLabel、imageView的frame值根本不会改变按钮里面图片在左而文字在右的格局。 要想实现功能就必须使用另外两个属性:github
@property(nonatomic) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsZero @property(nonatomic) UIEdgeInsets imageEdgeInsets; // default is UIEdgeInsetsZero
这两个属性是分别用来调整按钮中文本的偏移缩进以及图片的偏移缩进的,他们都是一个UIEdgeInsets对象,默认的值都是0,也就是默认的值都是0的状况下按钮的图片和文字垂直居中,并且图片在左边文字在右边,并且图片文本总体水平居中。而咱们则能够经过调整titleEdgeInsets和imageEdgeInsets的值来实现咱们想要的任何图文布局的状况,甚至咱们但愿图片和文字之间还要保留一些间隔的状况。怎么调整? 调整多少为最合适?ide
在调整以前咱们先定义几个特定的变量值:布局
CGRect titleRect = titleLabel.frame; //文本控件在按钮中的frame值。 CGRect imageRect = imageView.frame; //图片控件在按钮中的frame值。 CGFloat padding; //用于指定文本和图片的间隔值。 CGFloat selfWidth; //按钮控件的宽度 CGFloat selfHeight; //按钮控件的高度 CGFloat totalHeight=titleRect.size.height+padding+imageRect.size.height; //图文上下布局时所占用的总高度,注意这里也算上他们之间的间隔值padding
咱们能够经过更改按钮的titleEdgeInsets和imageEdgeInsets的值调整文本和图片的位置。若是咱们想往右移动20的话,那么就应该同时设置UIEdgeInsets的left=20, right=-20,而若是咱们想往上移动20的话,那么就应该应该同时设置UIEdgeInsets的top=-20,bottom=20。下面咱们就分别经过调整按钮的titleEdgeInsets和imageEdgeInsets的值来实现各类图文结合的效果:测试
这种方式是按钮默认的图文布局方式,由于要调整图片和文本的间距,因此只须要文本右移padding/2而图片左移padding/2值就能够了。设置的代码为:atom
titleEdgeInsets =UIEdgeInsetsMake(0, padding/2, 0, -padding/2); imageEdgeInsets = UIEdgeInsetsMake(0, -padding/2, 0, padding/2);
要实现这种布局只须要将文字往左偏移图片的宽度而且再往左偏移padding/2就能够了,而图片则只须要往右偏移文本的宽度并再往右偏移padding/2就能够了。设置的代码为:spa
titleEdgeInsets =UIEdgeInsetsMake(0, -(imageRect.size.width + padding/2), 0, (imageRect.size.width + padding/2)); imageEdgeInsets =UIEdgeInsetsMake(0, (titleRect.size.width+ padding/2), 0, -(titleRect.size.width+ padding/2));
这种布局下当图片和文字要求垂直居中后,新的图片的顶部位置应该等于(selfHeight - totalHeight)/2, 所以垂直须要偏移的值就是新的位置减去原来的位置imageRect.origin.y;而新的图片的水平中心点要等于selfWidth/2,而原来的图片的水平中心点等于imageRect.origin.x + imageRect.size.width/2,二者相减就是水平须要偏移的值。而新的文本的顶部位置应该等于新的图片的顶部位置(selfHeight - totalHeight)/2 + 图片的高度imageRect.size.height + 间隔padding ,所以垂直须要偏移的值就是新的顶部值减去原来的顶部位置titleRect.origin.y; 而新的文本的水平中心点也是selfWidth/2,而原来的文本的水平中心点是titleRect.origin.x + titleRect.size.width/2, 二者相减就是水平须要偏移的值,又由于默认的状况下当按钮比较小时会自动保留图片的尺寸和将文字部分缩小,由于当咱们实现文字和图片上下布局时,须要将文字的区域扩展到整个按钮部分,不然将会缩小按钮的文字的宽度,由于按钮的宽度为selfWidth,而文字的默认宽度是titleRect.size.width,因此上面的实现将文本移到中间后还须要分别向两边进行拉伸(selfWith - titleRect.size.width)/2来保证文本填充满全部的按钮区域,在下面的各类样式中凡是文字和图片垂直居中的状况下都要考虑这种状况 设置的代码为:设计
titleEdgeInsets =UIEdgeInsetsMake(((selfHeight - totalHeight)/2 + imageRect.size.height + padding - titleRect.origin.y), (selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2, -((selfHeight - totalHeight)/2 + imageRect.size.height + padding - titleRect.origin.y), -(selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2); imageEdgeInsets =UIEdgeInsetsMake(((selfHeight - totalHeight)/2 - imageRect.origin.y), (selfWidth /2 - imageRect.origin.x - imageRect.size.width /2), -((selfHeight - totalHeight)/2 - imageRect.origin.y), -(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
这种布局就是上面的文字和图片位置调换,所以设置代码为:code
titleEdgeInsets =UIEdgeInsetsMake(((selfHeight - totalHeight)/2 - titleRect.origin.y), (selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2, -((selfHeight - totalHeight)/2 - titleRect.origin.y), -(selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2); imageEdgeInsets =UIEdgeInsetsMake(((selfHeight - totalHeight)/2 + titleRect.size.height + padding - imageRect.origin.y), (selfWidth /2 - imageRect.origin.x - imageRect.size.width /2), -((selfHeight - totalHeight)/2 + titleRect.size.height + padding - imageRect.origin.y), -(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
这种方式要求图片在按钮中居中,而文字则要求左右居中而垂直方向位置则是距离按钮顶部的间隔值。 上面由于描述了水平居中的调整,所以这里就不介绍了,只介绍垂直方向的调整。 由于要求图片要垂直居中,所以不须要调整垂直偏移。而文本则要调整为距离顶部的间隔值,也就是新的文本的顶部值等于padding, 而原来顶部值是titleRect.origin.y,所以只须要偏移titleRect.origin.y - padding就能够了。设置代码为:orm
titleEdgeInsets =UIEdgeInsetsMake(-(titleRect.origin.y - padding), (selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2, (titleRect.origin.y - padding), -(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2); imageEdgeInsets =UIEdgeInsetsMake(0, (selfWidth /2 - imageRect.origin.x - imageRect.size.width /2), 0, -(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
这种方式要求图片在按钮居中,而文字则要求左右居中而垂直方向的底部位置则是距离按钮底部的间隔值。图片的调整上面有介绍,而文字的水平调整上面也有说到,这里面只说文字的垂直调整。文字新的底部位置等于selfHeight - padding, 而旧的底部位置是titleRect.size.height + titleRect.origin.y, 所以要偏移的位置就是二者相减的值。代码的设置为:
titleEdgeInsets =UIEdgeInsetsMake((selfHeight - padding - titleRect.origin.y - titleRect.size.height), (selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2, -(selfHeight - padding - titleRect.origin.y - titleRect.size.height), -(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2); imageEdgeInsets =UIEdgeInsetsMake(0, (selfWidth /2 - imageRect.origin.x - imageRect.size.width /2), 0, -(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
这种方式要求图片在按钮居中,而文字则要求左右居中而且在垂直在图片的上面并保留出padding的间隔。 图片的偏移上面有说到,而文字的水平偏移上面也有说到,这里只说垂直偏移,文字新的底部位置等于图片的顶部位置 - padding 而文字老的底部位置等于titleRect.size.height + titleRect.origin.y, 所以二者的差值就是文字须要垂直偏移的值。代码设置为:
titleEdgeInsets =UIEdgeInsetsMake(-(titleRect.origin.y + titleRect.size.height - imageRect.origin.y + padding), (selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2, (titleRect.origin.y + titleRect.size.height - imageRect.origin.y + padding), -(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2); imageEdgeInsets =UIEdgeInsetsMake(0, (selfWidth /2 - imageRect.origin.x - imageRect.size.width /2), 0, -(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
这种方式要求图片在按钮居中,而文字则要求左右居中而且垂直在图片的下面并保留出padding的间隔。图片的偏移上面有说到,而文字的水平偏移上面也有说到,这里只说垂直偏移,文字新的顶部位置等于imageRect.origin.y + imageRect.size.height + padding, 而文字老的顶部位置等于titleRect.origin.y,所以二者的差值就是文字须要垂直偏移的值。代码设置为:
titleEdgeInsets =UIEdgeInsetsMake((imageRect.origin.y + imageRect.size.height - titleRect.origin.y + padding), (selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2, -(imageRect.origin.y + imageRect.size.height - titleRect.origin.y + padding), -(selfWidth /2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) /2); imageEdgeInsets =UIEdgeInsetsMake(0, (selfWidth /2 - imageRect.origin.x - imageRect.size.width /2), 0, -(selfWidth /2 - imageRect.origin.x - imageRect.size.width /2));
在这种方式中,图片和文本都是垂直居中对齐,这部分是不须要调整的,而文本的左边则须要由原来的titleRect.origin.x移动到左边padding的位置,而图片的左边则须要由原来的imageRect.origin.x移动到selfWidth - padding - imageRect.size.width位置。所以代码设置为:
self.titleEdgeInsets = UIEdgeInsetsMake(0, -(titleRect.origin.x - padding), 0, (titleRect.origin.x - padding)); self.imageEdgeInsets = UIEdgeInsetsMake(0, (selfWidth - padding - imageRect.origin.x - imageRect.size.width), 0, -(selfWidth - padding - imageRect.origin.x - imageRect.size.width));
这种方式中,图片和文字的垂直位置不须要调整,而只须要将图文的水平位置调整便可,而调整的方法和上面的类似,只是图片移到左边儿文字移到右边而已。代码设置为:
self.titleEdgeInsets = UIEdgeInsetsMake(0, (selfWidth - padding - titleRect.origin.x - titleRect.size.width), 0, -(selfWidth - padding - titleRect.origin.x - titleRect.size.width)); self.imageEdgeInsets = UIEdgeInsetsMake(0, -(imageRect.origin.x - padding), 0, (imageRect.origin.x - padding));
前面说的的十种图文结合样式,我想应该能够知足您的需求了,若是这些图文结合的样式仍是没法知足您的需求时则您仍是别用UIButton了。
为了方便你们的使用,我把上面的图文结合样式整理成了一个UIButton的分类方法,你们能够直接拷贝使用:
头文件:
// // UIButton+ImageTitleStyle.h // // Created by 欧阳大哥 on 14-7-13. // QQ:156355113 // Github: https://github.com/youngsoft // Email: obq0387_cn@sina.com // #import <UIKit/UIKit.h> /* 针对同时设置了Image和Title的场景时UIButton中的图片和文字的关系 */ typedef NS_ENUM(NSInteger, ButtonImageTitleStyle ) { ButtonImageTitleStyleDefault = 0, //图片在左,文字在右,总体居中。 ButtonImageTitleStyleLeft = 0, //图片在左,文字在右,总体居中。 ButtonImageTitleStyleRight = 2, //图片在右,文字在左,总体居中。 ButtonImageTitleStyleTop = 3, //图片在上,文字在下,总体居中。 ButtonImageTitleStyleBottom = 4, //图片在下,文字在上,总体居中。 ButtonImageTitleStyleCenterTop = 5, //图片居中,文字在上距离按钮顶部。 ButtonImageTitleStyleCenterBottom = 6, //图片居中,文字在下距离按钮底部。 ButtonImageTitleStyleCenterUp = 7, //图片居中,文字在图片上面。 ButtonImageTitleStyleCenterDown = 8, //图片居中,文字在图片下面。 ButtonImageTitleStyleRightLeft = 9, //图片在右,文字在左,距离按钮两边边距 ButtonImageTitleStyleLeftRight = 10, //图片在左,文字在右,距离按钮两边边距 }; @interface UIButton (ImageTitleStyle) /* 调整按钮的文本和image的布局,前提是title和image同时存在才会调整。 padding是调整布局时整个按钮和图文的间隔。 */ -(void)setButtonImageTitleStyle:(ButtonImageTitleStyle)style padding:(CGFloat)padding; @end
实现文件:
// // UIButton+ImageTitleStyle.m // // Created by 欧阳大哥 on 14-7-13. // #import "UIButton+ImageTitleStyle.h" @implementation UIButton (ImageTitleStyle) -(void)setButtonImageTitleStyle:(ButtonImageTitleStyle)style padding:(CGFloat)padding { if (self.imageView.image != nil && self.titleLabel.text != nil) { //先还原 self.titleEdgeInsets = UIEdgeInsetsZero; self.imageEdgeInsets = UIEdgeInsetsZero; CGRect imageRect = self.imageView.frame; CGRect titleRect = self.titleLabel.frame; CGFloat totalHeight = imageRect.size.height + padding + titleRect.size.height; CGFloat selfHeight = self.frame.size.height; CGFloat selfWidth = self.frame.size.width; switch (style) { case ButtonImageTitleStyleLeft: if (padding != 0) { self.titleEdgeInsets = UIEdgeInsetsMake(0, padding/2, 0, -padding/2); self.imageEdgeInsets = UIEdgeInsetsMake(0, -padding/2, 0, padding/2); } break; case ButtonImageTitleStyleRight: { //图片在右,文字在左 self.titleEdgeInsets = UIEdgeInsetsMake(0, -(imageRect.size.width + padding/2), 0, (imageRect.size.width + padding/2)); self.imageEdgeInsets = UIEdgeInsetsMake(0, (titleRect.size.width+ padding/2), 0, -(titleRect.size.width+ padding/2)); } break; case ButtonImageTitleStyleTop: { //图片在上,文字在下 self.titleEdgeInsets = UIEdgeInsetsMake(((selfHeight - totalHeight)/2 + imageRect.size.height + padding - titleRect.origin.y), (selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) / 2, -((selfHeight - totalHeight)/2 + imageRect.size.height + padding - titleRect.origin.y), -(selfWidth/2 - titleRect.origin.x - titleRect.size.width /2) - (selfWidth - titleRect.size.width) / 2); self.imageEdgeInsets = UIEdgeInsetsMake(((selfHeight - totalHeight)/2 - imageRect.origin.y), (selfWidth /2 - imageRect.origin.x - imageRect.size.width / 2), -((selfHeight - totalHeight)/2 - imageRect.origin.y), -(selfWidth /2 - imageRect.origin.x - imageRect.size.width / 2)); } break; case ButtonImageTitleStyleBottom: { //图片在下,文字在上。 self.titleEdgeInsets = UIEdgeInsetsMake(((selfHeight - totalHeight)/2 - titleRect.origin.y), (selfWidth/2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2, -((selfHeight - totalHeight)/2 - titleRect.origin.y), -(selfWidth/2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2); self.imageEdgeInsets = UIEdgeInsetsMake(((selfHeight - totalHeight)/2 + titleRect.size.height + padding - imageRect.origin.y), (selfWidth /2 - imageRect.origin.x - imageRect.size.width / 2), -((selfHeight - totalHeight)/2 + titleRect.size.height + padding - imageRect.origin.y), -(selfWidth /2 - imageRect.origin.x - imageRect.size.width / 2)); } break; case ButtonImageTitleStyleCenterTop: { self.titleEdgeInsets = UIEdgeInsetsMake(-(titleRect.origin.y - padding), (selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2, (titleRect.origin.y - padding), -(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2); self.imageEdgeInsets = UIEdgeInsetsMake(0, (selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2), 0, -(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2)); } break; case ButtonImageTitleStyleCenterBottom: { self.titleEdgeInsets = UIEdgeInsetsMake((selfHeight - padding - titleRect.origin.y - titleRect.size.height), (selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2, -(selfHeight - padding - titleRect.origin.y - titleRect.size.height), -(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2); self.imageEdgeInsets = UIEdgeInsetsMake(0, (selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2), 0, -(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2)); } break; case ButtonImageTitleStyleCenterUp: { self.titleEdgeInsets = UIEdgeInsetsMake(-(titleRect.origin.y + titleRect.size.height - imageRect.origin.y + padding), (selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2, (titleRect.origin.y + titleRect.size.height - imageRect.origin.y + padding), -(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2); self.imageEdgeInsets = UIEdgeInsetsMake(0, (selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2), 0, -(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2)); } break; case ButtonImageTitleStyleCenterDown: { self.titleEdgeInsets = UIEdgeInsetsMake((imageRect.origin.y + imageRect.size.height - titleRect.origin.y + padding), (selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2, -(imageRect.origin.y + imageRect.size.height - titleRect.origin.y + padding), -(selfWidth / 2 - titleRect.origin.x - titleRect.size.width / 2) - (selfWidth - titleRect.size.width) / 2); self.imageEdgeInsets = UIEdgeInsetsMake(0, (selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2), 0, -(selfWidth / 2 - imageRect.origin.x - imageRect.size.width / 2)); } break; case ButtonImageTitleStyleRightLeft: { //图片在右,文字在左,距离按钮两边边距 self.titleEdgeInsets = UIEdgeInsetsMake(0, -(titleRect.origin.x - padding), 0, (titleRect.origin.x - padding)); self.imageEdgeInsets = UIEdgeInsetsMake(0, (selfWidth - padding - imageRect.origin.x - imageRect.size.width), 0, -(selfWidth - padding - imageRect.origin.x - imageRect.size.width)); } break; case ButtonImageTitleStyleLeftRight: { //图片在左,文字在右,距离按钮两边边距 self.titleEdgeInsets = UIEdgeInsetsMake(0, (selfWidth - padding - titleRect.origin.x - titleRect.size.width), 0, -(selfWidth - padding - titleRect.origin.x - titleRect.size.width)); self.imageEdgeInsets = UIEdgeInsetsMake(0, -(imageRect.origin.x - padding), 0, (imageRect.origin.x - padding)); } break; default: break; } } else { self.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0); self.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 0); } } @end
上面的方法setButtonImageTitleStyle:(ButtonImageTitleStyle)style padding:(CGFloat)padding
请在创建完UIButton对象而且指定一个具体的frame值或者自动布局的约束尺寸后,而且调用setTitle:forState:
和setImage:forSate:
后再调用:
UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(0,0,100,100)]; [button setTitle:@"测试文本" forState:UIControlStateNormal]; [button setImage:[UIImage imageNamed:@"test"] forState:UIControlStateNormal]; [button setButtonImageTitleStyle:ButtonImageTitleStyleTop padding:10];
另外若是你想要你的按钮中的图片和文字总体的水平居左,或者水平居右,或者垂直居上或者垂直居下则能够用UIButton的原生(UIControl)属性:
@property(nonatomic) UIControlContentVerticalAlignment contentVerticalAlignment; // how to position content vertically inside control. default is center @property(nonatomic) UIControlContentHorizontalAlignment contentHorizontalAlignment; // how to position content hozontally inside control. default is center
这两个属性来设置按钮的垂直和水平的总体位置的调整,具体设置则读者自行去实践吧。