追踪按钮的titleView与imgView布局

引用:
iOS的UIButton是一个非常常见而且常用的控件,我们一般用他来实现某个功能的提交以及选择操作。我们可以建立只有文字的Button,也可以建立只有图片的Button,具体的需求要看界面设计的具体情况。有时候我们希望应用的界面元素是丰富多彩的,有时候希望建立一个图文结合的控件来响应用户的手势操作,因此建立一个即有图片也有文字的按钮来实现功能,这个只需要分别调用UIButton的setTitle:forState:setImage:forSate:两个方法就可以实现具有图片和文字功能的按钮。但是系统默认的图文结合的按钮布局是:图片在左边而文字在右边,而且整体水平和垂直居中。比如下面这个图文按钮:

left.png

但是有的时候我们又希望图片在右边而文字在左边;

right.png

或者图片在上边而文字在下面;或者图片在按钮的中间而文字在图片的下面等等,但我们又不想放弃使用按钮这个控件,这时候怎么办? 事件总是能找到解决方法的, 有的人会先建立一个按钮控件铺在下面,而在上面分别覆盖一个UIImageViewUILabel来实现;而有的人则干脆在UIButton上建立一个UIImageViewUILabel两个子视图;而有的人则不会用UIButton来实现图文结合的功能。 前面说的几个方法看起来有效,也确实会解决问题,但缺点是代码量会增加,而且必须同时管理UIButton, UIImageView, UILabel这三个整体,如果哪天产品还希望有一个按钮按下高亮或者按下阴影效果时,你可能又要重新编写代码实现需求了。 那么我们是否要放弃UIButton呢?答案是否定的,其实UIButton本身是可以支持各种图文结合的,既然UIButton上能同时显示图片和文字,那就可以肯定的说UIButton里面本身一定有一个UIImageViewUILabel8子视图。UIButton*本身就是一个复合控件,他分别提供了两个属性:

@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值就能实现我们想要的图片文字的任何布局呢? 答案是肯定的.只是需要合适的契机.大多数人使用了另外一种方式:

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

通过这两个属性分别用来调整按钮中文本和图片的偏移缩进.来达到自己的布局效果.例如图片在上文字在下整体居中

图片在上文字在下整体居中.png
 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));

在某种特定情况下确实能达到效果.但是....

当button在normal状态下为0.在select状态下为2000.因为通过normal状态的文本和图片的宽高来固定的设定偏移.导致状态更改时.偏移未做更新.导致布局错乱

如下图:

normal与select长度不一致.布局错乱.gif

这种情况是normal状态下设定了固定的偏移值.但是随着文本长度变化.偏移值没有得到相应的变化所以布局错乱!

其实解决的方式还有一种:那就是继承自按钮在layoutSubviews中重新给按钮中的文本和图片布局即可!

图片在上文字在下整体居中.png
//图片居中,文字在下 距离按钮底部。
            self.imageView.FQ_centerX = self.FQ_width * 0.5;
            self.imageView.FQ_centerY = self.FQ_height * 0.5;
            
            self.titleLabel.FQ_width = self.FQ_width;
            self.titleLabel.FQ_y = self.FQ_height - self.titleLabel.FQ_height - self.padding;
            self.titleLabel.FQ_x = 0;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;

为了方便,笔者使用类拓展.运行时交换系统的方法,添加重新布局即可达到我们想要的效果多种效果

typedef NS_ENUM(NSInteger, FQ_ButtonImageTitleStyle ) {
    FQ_ButtonImageTitleStyleLeft  = 1,         //图片在左,文字在右,整体居中。
    FQ_ButtonImageTitleStyleRight     = 2,     //图片在右,文字在左,整体居中。
    FQ_ButtonImageTitleStyleTop  = 3,          //图片在上,文字在下,整体居中。
    FQ_ButtonImageTitleStyleBottom    = 4,     //图片在下,文字在上,整体居中。
    FQ_ButtonImageTitleStyleCenterTop = 5,     //图片居中,文字在上,距离按钮顶部。
    FQ_ButtonImageTitleStyleCenterBottom = 6,  //图片居中,文字在下,距离按钮底部。
    FQ_ButtonImageTitleStyleCenterUp = 7,      //图片居中,文字在图片上面,距离图片顶部。
    FQ_ButtonImageTitleStyleCenterDown = 8,    //图片居中,文字在图片下面,距离图片底部。
    FQ_ButtonImageTitleStyleRightLeft = 9,     //图片在右,文字在左,距离按钮两边边距
    FQ_ButtonImageTitleStyleLeftRight = 10,    //图片在左,文字在右,距离按钮两边边距
    FQ_ButtonImageTitleStyleFloatingTop = 11,  //图片在下,文字浮在上层,均居中
};
在layoutSubviews中重新布局.gif

附上代码:

#import 

/*
 针对同时设置了Image和Title的场景时UIButton中的图片和文字的关系
 */
typedef NS_ENUM(NSInteger, FQ_ButtonImageTitleStyle ) {
    FQ_ButtonImageTitleStyleLeft  = 1,         //图片在左,文字在右,整体居中。
    FQ_ButtonImageTitleStyleRight     = 2,     //图片在右,文字在左,整体居中。
    FQ_ButtonImageTitleStyleTop  = 3,          //图片在上,文字在下,整体居中。
    FQ_ButtonImageTitleStyleBottom    = 4,     //图片在下,文字在上,整体居中。
    
    FQ_ButtonImageTitleStyleCenterTop = 5,     //图片居中,文字在上,距离按钮顶部。
    FQ_ButtonImageTitleStyleCenterBottom = 6,  //图片居中,文字在下,距离按钮底部。
    
    
    FQ_ButtonImageTitleStyleCenterUp = 7,      //图片居中,文字在图片上面,距离图片顶部。
    FQ_ButtonImageTitleStyleCenterDown = 8,    //图片居中,文字在图片下面,距离图片底部。
    
    FQ_ButtonImageTitleStyleRightLeft = 9,     //图片在右,文字在左,距离按钮两边边距
    FQ_ButtonImageTitleStyleLeftRight = 10,    //图片在左,文字在右,距离按钮两边边距
    
    FQ_ButtonImageTitleStyleFloatingTop = 11,  //图片在下,文字浮在上层,均居中
};

@interface UIView (FQExtension)
@property (nonatomic, assign) CGFloat FQ_x;
@property (nonatomic, assign) CGFloat FQ_y;
@property (nonatomic, assign) CGFloat FQ_width;
@property (nonatomic, assign) CGFloat FQ_height;
@property (nonatomic, assign) CGFloat FQ_centerX;
@property (nonatomic, assign) CGFloat FQ_centerY;
@property (nonatomic, assign) CGFloat FQ_right;
@property (nonatomic, assign) CGFloat FQ_buttom;
@property (nonatomic, assign) CGSize  FQ_size;
@property (nonatomic, assign) CGPoint FQ_orign;

+ (instancetype)viewFromNib;
@end


@interface UIButton (ImageTitleSpacing)

@property (nonatomic, assign) FQ_ButtonImageTitleStyle style;

@property (nonatomic, assign) CGFloat padding;

@end

#import "UIButton+ImageTitleSpacing.h”
#import 


static NSString *fq_ButtonPadding = @“fq_ButtonPaddingKey”;
static NSString *fq_ButtonStyle = @“fq_ButtonStyleKey”;


@implementation UIView (FQExtension)
+ (instancetype)viewFromNib
{
    return [[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self) owner:nil options:nil] lastObject];
}

- (void)setFQ_x:(CGFloat)FQ_x
{
    CGRect frame = self.frame;
    frame.origin.x = FQ_x;
    self.frame = frame;
}

- (CGFloat)FQ_x
{
    return self.frame.origin.x;
}

- (void)setFQ_y:(CGFloat)FQ_y
{
    CGRect frame = self.frame;
    frame.origin.y = FQ_y;
    self.frame = frame;
}

- (CGFloat)FQ_y
{
    return self.frame.origin.y;
}

- (void)setFQ_width:(CGFloat)FQ_width
{
    CGRect frame = self.frame;
    frame.size.width = FQ_width;
    self.frame = frame;
}

- (CGFloat)FQ_width
{
    return self.frame.size.width;
}

- (void)setFQ_height:(CGFloat)FQ_height
{
    CGRect frame = self.frame;
    frame.size.height = FQ_height;
    self.frame = frame;
}

- (CGFloat)FQ_height
{
    return self.frame.size.height;
}

-(void)setFQ_centerX:(CGFloat)FQ_centerX
{
    CGPoint center = self.center;
    center.x = FQ_centerX;
    self.center = center;
}

- (CGFloat)FQ_centerX
{
    return self.center.x;
}

-(void)setFQ_centerY:(CGFloat)FQ_centerY
{
    CGPoint center = self.center;
    center.y = FQ_centerY;
    self.center = center;
}

- (CGFloat)FQ_centerY
{
    return self.center.y;
}

- (void)setFQ_right:(CGFloat)FQ_right
{
    self.FQ_x = FQ_right - self.FQ_width;
}

- (CGFloat)FQ_right
{
    return CGRectGetMaxX(self.frame);
}

- (void)setFQ_buttom:(CGFloat)FQ_buttom
{
    self.FQ_y = FQ_buttom - self.FQ_height;
}

- (CGFloat)FQ_buttom
{
    return CGRectGetMaxY(self.frame);
}

- (void)setFQ_size:(CGSize)FQ_size
{
    CGRect frame = self.frame;
    frame.size = FQ_size;
    self.frame = frame;
}

- (CGSize)FQ_size
{
    return self.frame.size;
}

- (void)setFQ_orign:(CGPoint)FQ_orign
{
    CGRect frame = self.frame;
    frame.origin = FQ_orign;
    self.frame = frame;
}
- (CGPoint)FQ_orign
{
    return self.frame.origin;
}
@end

@implementation UIButton (ImageTitleSpacing)

-(void)setPadding:(CGFloat)padding
{
    objc_setAssociatedObject(self, &fq_ButtonPadding, @(padding), OBJC_ASSOCIATION_ASSIGN);
}

-(CGFloat)padding
{
    return [objc_getAssociatedObject(self, &fq_ButtonPadding) floatValue];
}

-(FQ_ButtonImageTitleStyle)style
{
    return [objc_getAssociatedObject(self, &fq_ButtonStyle) integerValue];
}

-(void)setStyle:(FQ_ButtonImageTitleStyle)style
{
    objc_setAssociatedObject(self, &fq_ButtonStyle, @(style), OBJC_ASSOCIATION_ASSIGN);
}

//添加两个

+ (void)load {
    // 通过class_getInstanceMethod()函数从当前对象中的method list获取method结构体,如果是类方法就使用class_getClassMethod()函数获取。
    Method fromMethod = class_getInstanceMethod([self class], @selector(layoutSubviews));
    Method toMethod = class_getInstanceMethod([self class], @selector(swizzlingLayouFQubviews));
    /**
     *  我们在这里使用class_addMethod()函数对Method Swizzling做了一层验证,如果self没有实现被交换的方法,会导致失败。
     *  而且self没有交换的方法实现,但是父类有这个方法,这样就会调用父类的方法,结果就不是我们想要的结果了。
     *  所以我们在这里通过class_addMethod()的验证,如果self实现了这个方法,class_addMethod()函数将会返回NO,我们就可以对其进行交换了。
     */
    if (!class_addMethod([self class], @selector(swizzlingLayouFQubviews), method_getImplementation(toMethod), method_getTypeEncoding(toMethod))) {
        method_exchangeImplementations(fromMethod, toMethod);
    }
}


// 我们自己实现的方法,也就是和self的viewDidLoad方法进行交换的方法。
- (void)swizzlingLayouFQubviews {
    
    
    [self swizzlingLayouFQubviews];
    
    switch (self.style) {
        case FQ_ButtonImageTitleStyleLeft:
        {
            // 图片在左,标题在右
            
            self.imageView.FQ_x = (self.FQ_width - self.imageView.FQ_width - self.titleLabel.FQ_width - self.padding) * 0.5;
            self.titleLabel.FQ_x = self.imageView.FQ_right + self.padding;
            self.titleLabel.textAlignment = NSTextAlignmentLeft;
        }
            break;
        case FQ_ButtonImageTitleStyleRight:
        {
            // 图片在右,标题在左
            
            self.titleLabel.FQ_x = (self.FQ_width - self.imageView.FQ_width - self.titleLabel.FQ_width - self.padding) * 0.5;
            self.titleLabel.textAlignment = NSTextAlignmentLeft;
            
            self.imageView.FQ_x = self.titleLabel.FQ_right + self.padding;
        }
            break;
        case FQ_ButtonImageTitleStyleTop:
        {
            // 图片在上,文字在下,整体居中
            self.imageView.FQ_centerX = self.FQ_width * 0.5;
            self.imageView.FQ_y = (self.FQ_height - self.imageView.FQ_height - self.titleLabel.FQ_height - self.padding) * 0.5;
            
            self.titleLabel.FQ_width = self.FQ_width;
            self.titleLabel.FQ_y = self.imageView.FQ_buttom + self.padding;
            self.titleLabel.FQ_x = 0;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
        }
            break;
        case FQ_ButtonImageTitleStyleBottom:
        {
            //图片在下,文字在上,整体居中。
            self.titleLabel.FQ_width = self.FQ_width;
            self.titleLabel.FQ_y = (self.FQ_height - self.imageView.FQ_height - self.titleLabel.FQ_height - self.padding) * 0.5;
            self.titleLabel.FQ_x = 0;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
            
            self.imageView.FQ_centerX = self.FQ_width * 0.5;
            self.imageView.FQ_y = self.titleLabel.FQ_buttom + self.padding;
        }
            break;
        case FQ_ButtonImageTitleStyleCenterTop:
        {
            //图片居中,文字在上 距离按钮顶部。
            self.imageView.FQ_centerX = self.FQ_width * 0.5;
            self.imageView.FQ_centerY = self.FQ_height * 0.5;
            
            self.titleLabel.FQ_width = self.FQ_width;
            self.titleLabel.FQ_y = self.padding;
            self.titleLabel.FQ_x = 0;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
        }
            break;
        case FQ_ButtonImageTitleStyleCenterBottom:
        {
            //图片居中,文字在下 距离按钮底部。
            self.imageView.FQ_centerX = self.FQ_width * 0.5;
            self.imageView.FQ_centerY = self.FQ_height * 0.5;
            
            self.titleLabel.FQ_width = self.FQ_width;
            self.titleLabel.FQ_y = self.FQ_height - self.titleLabel.FQ_height - self.padding;
            self.titleLabel.FQ_x = 0;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
        }
            break;
        case FQ_ButtonImageTitleStyleCenterUp:
        {
            //图片居中,文字在图片上面 距离图片顶部。
            self.imageView.FQ_centerX = self.FQ_width * 0.5;
            self.imageView.FQ_centerY = self.FQ_height * 0.5;
            
            self.titleLabel.FQ_width = self.FQ_width;
            self.titleLabel.FQ_y = self.imageView.FQ_y - self.padding - self.titleLabel.FQ_height;
            self.titleLabel.FQ_x = 0;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
        }
            break;
        case FQ_ButtonImageTitleStyleCenterDown:
        {
            
            //图片居中,文字在图片下面 距离图片底部。
            self.imageView.FQ_centerX = self.FQ_width * 0.5;
            self.imageView.FQ_centerY = self.FQ_height * 0.5;
            
            self.titleLabel.FQ_width = self.FQ_width;
            self.titleLabel.FQ_y = self.imageView.FQ_buttom + self.padding;
            self.titleLabel.FQ_x = 0;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
        }
            break;
        case FQ_ButtonImageTitleStyleRightLeft:
        {
            //图片在右,文字在左,距离按钮两边边距
            self.imageView.FQ_x = self.padding;
            self.imageView.FQ_centerY = self.FQ_height * 0.5;
        
            self.titleLabel.FQ_centerY = self.FQ_height * 0.5;
            self.titleLabel.FQ_x = self.FQ_width - self.padding - self.titleLabel.FQ_width;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
        }
            break;
        case FQ_ButtonImageTitleStyleLeftRight:
        {
            
            //图片在左,文字在右,距离按钮两边边距
            self.titleLabel.FQ_x = self.padding;
            self.titleLabel.FQ_centerY = self.FQ_height * 0.5;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
        
            self.imageView.FQ_centerY = self.FQ_height * 0.5;
            self.imageView.FQ_x = self.FQ_width - self.padding - self.imageView.FQ_width;
            
        }
            break;
        case FQ_ButtonImageTitleStyleFloatingTop:
        {
            //图片在下, 文字浮在上层, 均居中
            self.imageView.FQ_centerY = self.FQ_height * 0.5;
            self.imageView.FQ_centerX = self.FQ_width * 0.5;
            
            self.titleLabel.FQ_centerX = self.FQ_width * 0.5;
            self.titleLabel.FQ_centerY = self.FQ_height * 0.5;
            self.titleLabel.textAlignment = NSTextAlignmentCenter;
            
        }
            break;
        default:
            break;
    }
    
}

@end

参考:UIButton实现各种图文结合的效果以及原理<https://juejin.im/post/5a91723c6fb9a0634c268a6e>

你可能感兴趣的:(追踪按钮的titleView与imgView布局)