zoukankan      html  css  js  c++  java
  • iOS button 里边图片和文字的距离

    很多次,系统默认的UIButton不能满足需求,每次都是查了很多资料,最后还是用最复杂的方式来修改button的标题和图片的位置,今天,花些时间来梳理一下这方面的知识...

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

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

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

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


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

    - (CGRect)titleRectForContentRect:(CGRect)contentRect;
    - (CGRect)imageRectForContentRect:(CGRect)contentRect;


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

    一,设置属性

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

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

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


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

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

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

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

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

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

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

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

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

    然后设置UIButton的属性:

    button.imageEdgeInsets = UIEdgeInsetsMake(0,titleSize.width, 0, -titleSize.width);
    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一致(只是为了提前使用一次):

    button.titleLabel.backgroundColor = button.backgroundColor;
    button.imageView.backgroundColor = button.backgroundColor;


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

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

    CGFloat interval = 1.0;
    

    然后设置button的两个属性:

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


    效果如下:

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

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

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

    最终效果图:



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

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

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


    效果图:

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

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

    二,通过自定义Button

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

    - (CGRect)titleRectForContentRect:(CGRect)contentRect;
    - (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的高度,即:

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


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

    然后设置imageView的frame:

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

    即:

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


    下面来设置titleLabel的frame:

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

    效果图:


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

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



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

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

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

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


    效果图:

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

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

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


    我重写了它的setter方法:

    - (void)setLzType:(LZRelayoutButtonType)lzType {
        _lzType = lzType;
        
        if (lzType != LZRelayoutButtonTypeNomal) {
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
        }
    }


    这种方式的设置,我写了一个demo:github地址,里面的自定义button可以拿来直接使用,如果对你有帮助,还请star支持一下,感谢!!!

    总结:以上两种方式都能实现重新布局UIButton的子控件的效果,各有优缺点:

    第一种方式,需要精确的设置偏移量,但是有些量是无法获取的,只能在使用时调整,特别是设置标题在底部时,总感觉image的左右距离button的左右间隙不一致;

    第二种方式,对frame的控制就比较自由了,需要注意的是对间隔把控,使用这种方式就没有第一种的方式问题了;

    不管是系统默认,还是我们修改之后的,button的frame设置都是很重要的,不合适的frame会让button看起来很不舒服...

  • 相关阅读:
    mysql报错排查总结
    java设计模式--外观模式
    java设计模式--策略模式
    java设计模式--策略模式
    java设计模式--简单工厂
    java设计模式--简单工厂
    国外有哪些比较好的IT社区
    使用jmeter进行性能测试-Jmeter教程及技巧汇总 (转)
    Fiddler环境配置教程
    Fiddler+Jmeter+断言详细教程
  • 原文地址:https://www.cnblogs.com/yecong/p/6112227.html
Copyright © 2011-2022 走看看