UIButton的默认布局是:title在右,image在左;

很多时候我们需要的是title在左边,或者title在下面,这时就需要调整UIButton的TitleLabel和ImageView的位置了,查了很多资料,要么零零散散的介绍,要么就是特别复杂的实现;经过一段时间的学习,在这里总结一下实现的方式;

一种是设置UIButton的以下两个属性:

  1. @property(nonatomic) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsZero
  2. @property(nonatomic) UIEdgeInsets imageEdgeInsets; // default is UIEdgeInsetsZero

还有一种就是,自定义button,继承自UIButton,重写UIButton的以下两个方法:

  1. - (CGRect)titleRectForContentRect:(CGRect)contentRect;
  2. - (CGRect)imageRectForContentRect:(CGRect)contentRect;

下面来介绍这两种实现方式:

一,设置属性

1.修改为标题在左,图片在右样式

其属性的类型为UIEdgeInsets,为一个结构体:

  1. typedef struct UIEdgeInsets {
  2. CGFloat top, left, bottom, right;  // specify amount to inset (positive) for each of the edges. values can be negative to 'outset'
  3. } UIEdgeInsets;

表示 上 左 下 右的偏移量;解释下,这四个量的含义:

top : 为正数的时候,是往下偏移,为负数的时候往上偏移;

left : 为正数的时候往右偏移,为负数的时候往左偏移;

bottom : 为正数的时候往上偏移,为负数的时候往下偏移;

right :为正数的时候往左偏移,为负数的时候往右偏移;

在设置UIButton的这两个属性时,遇到的第一个问题就是偏移量设置为多少?很多介绍这种方法的使用的都是固定值,当按钮的frame改变时,需要改动很多地方,看的有点云里雾里的;

经过一些研究,发现还是有一些公共的东西可以使用的:

第一个想到的是UIButton的标题titleLabel的frame和imageView的frame,所以,先获取titleLabel和imageView的size:

  1. CGSize titleSize = button.titleLabel.bounds.size;
  2. CGSize imageSize = button.imageView.bounds.size;

然后设置UIButton的属性:

  1. button.imageEdgeInsets = UIEdgeInsetsMake(0,titleSize.width, 0, -titleSize.width);
  2. button.titleEdgeInsets = UIEdgeInsetsMake(0, -imageSize.width, 0, imageSize.width);

因为这两个属性的默认值都是0,所以,在不需要偏移的方向上,偏移量设置为0即可,那么,怎么知道哪个方向需要偏移,哪个方向不需要偏移呢?其实,很简单,只需要仔细观察,偏移前(系统默认布局)和偏移后(你想要的布局)有哪些变化;

对于image:由左边移动到右边,可知,上下不变,左右偏移,即image的left和right变化;

对于title:由右边移动到左边,同样是上下不变,左右偏移,即title的left和right变化;

下面解释一下需要改变的方向偏移量设置的含义:

因为要把image移动到button的右边,需要往右移动,所以imageEdgeInsets距左边界的偏移量设置为标题的宽度,即:titleSize.width,右边的偏移量同样是titleSize.width,但是应该是负的,其他方向没有移动,直接设为默认值0;

同理,title需要往左移动,需要设置titleEdgeInsets距离左边界的偏移量为负的image的宽度,即:-imageSize.width
这样,同样,此时title距离右边界的偏移量就不是0了,而应该是image的宽度,即:imageSize.width;

这样就设置完了,但是结果好像并不是那么理想:

仔细查找后发现,获取到的titleSize的为0:

查了一些资料,没有找到相关的解释,暂时也没搞清楚是什么原因,如果你知道,还请留言告知,感谢!

但是,偶然间发现,只要在获取titleSize之前,使用一次button的titleLabel和imageView,就能获取到他的size了,设置一下titleLabel和imageView的任意属性都行,如果不需要设置这些属性,可以和我一样,设置一下它的背景色和button一致(只是为了提前使用一次):

  1. button.titleLabel.backgroundColor = button.backgroundColor;
  2. button.imageView.backgroundColor = button.backgroundColor;

PS:这样虽然能够获取到titleSize,但是多这么一个操作,实在不是正常的,如果你有更好的方式获取,还请留言告知,感谢!!

这样设置之后,基本能够实现需求了,但是子控件之间是有间隙的,这里我设置了1像素的宽度:

  1. CGFloat interval = 1.0;

然后设置button的两个属性:

  1. button.imageEdgeInsets = UIEdgeInsetsMake(0,titleSize.width + interval, 0, -(titleSize.width + interval));
  2. button.titleEdgeInsets = UIEdgeInsetsMake(0, -(imageSize.width + interval), 0, imageSize.width + interval);

效果如下:

会发现,几乎和系统默认的一致;

最终实现完整设置(主要是获取size的时机)为:

  1. button.titleLabel.backgroundColor = button.backgroundColor;
  2. button.imageView.backgroundColor = button.backgroundColor;
  3. //在使用一次titleLabel和imageView后才能正确获取titleSize
  4. CGSize titleSize = button.titleLabel.bounds.size;
  5. CGSize imageSize = button.imageView.bounds.size;
  6. CGFloat interval = 1.0;
  7. button.imageEdgeInsets = UIEdgeInsetsMake(0,titleSize.width + interval, 0, -(titleSize.width + interval));
  8. button.titleEdgeInsets = UIEdgeInsetsMake(0, -(imageSize.width + interval), 0, imageSize.width + interval);

最终效果图:

2. 修改为,图片在上,标题在下样式

过程基本相同,只是在最后设置的时候不同,这里直接给出主要设置代码:

  1. button.titleLabel.backgroundColor = button.backgroundColor;
  2. button.imageView.backgroundColor = button.backgroundColor;
  3. CGSize titleSize = button.titleLabel.bounds.size;
  4. CGSize imageSize = button.imageView.bounds.size;
  5. CGFloat interval = 1.0;
  6. [button setImageEdgeInsets:UIEdgeInsetsMake(0,0, titleSize.height + interval, -(titleSize.width + interval))];
  7. [button setTitleEdgeInsets:UIEdgeInsetsMake(imageSize.height + interval, -(imageSize.width + interval), 0, 0)];

效果图:

在设置偏移量的时候,误差还是有的,经过测试,最好的解决方式是,button的大小设置,要恰到好处能够容下标题和图片,对于追求完美的人,可自己修改偏移参数;

针对此方法,本人写了一个UIButton的category:Demo地址,里面的类目可直接拿来使用,简单调用一个方法即可;

二,通过自定义Button

该方法,是通过自定义一个button,继承自UIButton,然后重写方法:

  1. - (CGRect)titleRectForContentRect:(CGRect)contentRect;
  2. - (CGRect)imageRectForContentRect:(CGRect)contentRect;

对于这两个方法,网上的一些介绍真是不得不吐槽了,模糊不清;后来根据个人尝试,个人理解为:方法的参数contentRect,即内容的frame,其值和button的bounds是一样的,通过这个参数可以获取到当前button的size;返回值为CGRect类型,即是title或image在button的绝对坐标值;换句话说,这里返回的是一个绝对坐标,即button的子控件在button上的绝对布局,这里可以返回一个写死的frame(查到的使用此方法的也都是写死的frame),但要注意,不要超过contentRect的范围;

有一点需要说明:这两个方法不是只调用一次,会被多次调用,只要button的title改变,都会调用此方法,最后一次调用,返回的frame值,才是最终的布局frame,所以,在这里,可以通过获取button的标题,动态地修改其frame,使title和image显示紧凑;

明白了这两个方法,下面就开始使用它:

1.标题在左侧,图像在右侧

一般这种布局的不同都是宽度比高大很多,所以,这里我以button的高度为参考来设置imageView和titleLabel的高度,即:

  1. CGFloat inteval = CGRectGetHeight(contentRect)/8.0;
  2. //设置图片的宽高为button高度的3/4;
  3. CGFloat imageH = CGRectGetHeight(contentRect) - 22 * inteval;

这个间隔可以根据自己的需求修改;

然后设置imageView的frame:

  1. CGRect rect = CGRectMake(CGRectGetWidth(contentRect) - imageH - inteval, inteval, imageH, imageH);

即:

  1. - (CGRect)imageRectForContentRect:(CGRect)contentRect {
  2. CGFloat inteval = CGRectGetHeight(contentRect)/8.0;
  3. //设置图片的宽高为button高度的3/4;
  4. CGFloat imageH = CGRectGetHeight(contentRect) - 22 * inteval;
  5. CGRect rect = CGRectMake(CGRectGetWidth(contentRect) - imageH - inteval, inteval, imageH, imageH);
  6. return rect;
  7. }

下面来设置titleLabel的frame:

  1. - (CGRect)titleRectForContentRect:(CGRect)contentRect {
  2. CGFloat inteval = CGRectGetHeight(contentRect)/8.0;
  3. //设置图片的宽高为button高度的3/4;
  4. CGFloat imageH = CGRectGetHeight(contentRect) - 22 * inteval;
  5. CGRect rect = CGRectMake(inteval, inteval, CGRectGetWidth(contentRect) - imageH - 2*inteval, CGRectGetHeight(contentRect) - 2*inteval);
  6. return rect;
  7. }

效果图:


这种设置的方法好处是,可以手动控制titleLabel和imageView的frame,但是不足之处是,不能像系统的那样根据图片的scale和title字符串的大小来设置frame的大小;

当button的frame能够很好的包含title和image的时候,效果基本差不多;当button的frame不适合时区别还是很明显的:

可以看出,主要的区别是:系统默认的可以动态地修改子控件的frame,虽然显示效果也不是很理想;

2.标题在底部,图像在上面

思路基本一致,这里直接给出设置代码:

  1. - (CGRect)imageRectForContentRect:(CGRect)contentRect {
  2. CGFloat inteval = CGRectGetWidth(contentRect)/16.0;
  3. inteval = MIN(inteval, 6);
  4. //设置图片的宽高为button宽度的7/8;
  5. CGFloat imageW = CGRectGetWidth(contentRect) - 22 * inteval;
  6. CGRect rect = CGRectMake(inteval, inteval, imageW, imageW);
  7. return rect;
  8. }
  1. - (CGRect)titleRectForContentRect:(CGRect)contentRect {
  2. CGFloat inteval = CGRectGetWidth(contentRect)/16.0;
  3. inteval = MIN(inteval, 6);
  4. //设置图片的宽高为button宽度的7/8;
  5. CGFloat imageW = CGRectGetWidth(contentRect) - 22 * inteval;
  6. CGRect rect = CGRectMake(0, inteval*2 + imageW, CGRectGetWidth(contentRect) , CGRectGetHeight(contentRect) - 3*inteval - imageW);
  7. return rect;
  8. }

效果图:

这里有一点说明:就是标题的对齐方式,上面的效果图是设置了居中对齐,系统默认是居左的,什么时候更改这个设置呢?

我的做法是这样的,因为我增加了一个自定义属性:

  1. #import <UIKit/UIKit.h>
  2. typedef NS_ENUM(NSInteger,LZRelayoutButtonType) {
  3. LZRelayoutButtonTypeNomal  = 0,//默认
  4. LZRelayoutButtonTypeLeft   = 1,//标题在左
  5. LZRelayoutButtonTypeBottom = 2,//标题在下
  6. };
  7. @interface LZRelayoutButton : UIButton
  8. @property (assign,nonatomic)LZRelayoutButtonType lzType;
  9. @end

我重写了它的setter方法:

    1. - (void)setLzType:(LZRelayoutButtonType)lzType {
    2. _lzType = lzType;
    3. if (lzType != LZRelayoutButtonTypeNomal) {
    4. self.titleLabel.textAlignment = NSTextAlignmentCenter;
    5. }
    6. }

[iOS]详解调整UIButton的title和image的位置的更多相关文章

  1. IOS详解TableView——选项抽屉(天猫商品列表)

    在之前的有篇文章讲述了利用HeaderView来写类似QQ好友列表的表视图. 这里写的天猫抽屉其实也可以用该方法实现,具体到细节每个人也有所不同.这里采用的是点击cell对cell进行运动处理以展开“ ...

  2. iOS:详解MJRefresh刷新加载更多数据的第三方库

    原文链接:http://www.ios122.com/2015/08/mjrefresh/ 简介 MJRefresh这个第三方库是李明杰老师的杰作,这个框架帮助我们程序员减轻了超级多的麻烦,节约了开发 ...

  3. IOS详解TableView——内置刷新,EGO,以及搜索显示控制器

    内置刷新 内置刷新是苹果IOS6以后才推出的一个API,主要是针对TableViewController增加了一个属性,refreshControl,所以如果想用这个内置下拉刷新的话,最好给你的Tab ...

  4. iOS 详解NSXMLParser方法解析XML数据方法

    前一篇文章已经介绍了如何通过URL从网络上获取xml数据.下面介绍如何将获取到的数据进行解析. 下面先看看xml的数据格式吧! <?xml version="1.0" enc ...

  5. IOS详解TableView——对话聊天布局的实现

    上篇博客介绍了如何使用UITableView实现类似QQ的好友界面布局.这篇讲述如何利用自定义单元格来实现聊天界面的布局. 借助单元格实现聊天布局难度不大,主要要解决的问题有两个: 1.自己和其他人说 ...

  6. iOS详解MMDrawerController抽屉效果(一)

      提前说好,本文绝对不是教你如何使用MMDrawerController这个第三方库,因为那太多人写了 ,也太简单了.这篇文章主要带你分析MMDrawerController是怎么实现抽屉效果,明白 ...

  7. iOS 详解NSObject协议

      协议就是一组接口的集合,遵守一个协议之后就拥有的该协议中所有方法的声明.NSObject这个类遵守了NSObject协议,并且实现了NSObject协议里的所有方法,所以NSObject类及其子类 ...

  8. 第二十篇、自定义UIButton(设置title和image的位置)

    #import "CustomButton.h" #define ImageW 15 #define ImageH 15 #define KRadio 0.75 @implemen ...

  9. 深入PHP EOF(heredoc)用法详解

    介绍下使用EOF heredoc方式,输出长段内容的方法, <?php $name = '姓名'; print <<<EOT <html> <head> ...

随机推荐

  1. caffe︱cifar-10数据集quick模型的官方案例

    准备拿几个caffe官方案例用来练习,就看到了caffe中的官方案例有cifar-10数据集.于是练习了一下,在CPU情况下构建quick模型.主要参考博客:liumaolincycle的博客 配置: ...

  2. org.apache.jasper.JasperException: /pages/column.jsp (line: 8, column: 1) File "pathTags.jsp" not f

    1.错误描述 21-Mar-2015 00:57:40.934 INFO [localhost-startStop-2] org.apache.catalina.core.ApplicationCon ...

  3. weblogic部署web项目出现错误

    1.错误描述 <2015-3-15 下午02时13分01秒 CST> <Info> <Security> <BEA-090905> <Disabl ...

  4. windows驱动程序wdf--KMDF大致框架

    继WDM后微软出了WDF,封装了WDM中的一些基本代码逻辑.本人菜鸟,也不知道本质上有何区别,只觉得是多了Wdf开头的函数,基本的编程框架上有点出入. KMDF是WDF的内核级部分,为了理清KMDF的 ...

  5. freemarker.template.TemplateException:Macro has no such argument:params

    1.错误描述 freemarker.template.TemplateException:Macro mainSelect has no such argument:params 2.错误原因 在宏定 ...

  6. asp.net+jQueryRotate开发幸运大转盘

    在线抽奖程序在很多网站上很多,抽奖形式多种多样,Flash抽奖偏多,本文将给大家介绍jQuery转盘抽奖,结合代码实例将使用jQuery和asp.net来实现转盘抽奖程序,为了便于理解,文章贴出实现源 ...

  7. emWin 2天速成实例教程000_如何快速入门ucGUI/emWin

    备注:(1)打开工程目录下的"Exe\GUISimulationDebug.exe"即可看到效果.(2)看完教程000-005就基本会用emWin做项目,其他章节可以需要时再参考. ...

  8. css图片的相关操作

    css图片的相关操作 1.案例源码 <!DOCTYPE html><html lang="en"><head> <meta charset ...

  9. 【BZOJ2843】极地旅行社(Link-Cut Tree)

    [BZOJ2843]极地旅行社(Link-Cut Tree) 题面 BZOJ 题解 \(LCT\)模板题呀 没什么好说的了.. #include<iostream> #include< ...

  10. 【NOIP2015】运输计划(二分,差分)

    题面 Description 公元 2044 年,人类进入了宇宙纪元. L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P ...