iOS---UIButton详解及如何调整image和title的布局

@class UIImage, UIFont, UIColor, UIImageView, UILabel;

typedef NS_ENUM(NSInteger, UIButtonType) {

//以枚举的形似表示---button的界面形式

UIButtonTypeCustom = 0,                        // no button type

UIButtonTypeSystem NS_ENUM_AVAILABLE_IOS(7_0),  // standard system button

UIButtonTypeDetailDisclosure,

UIButtonTypeInfoLight,

UIButtonTypeInfoDark,

UIButtonTypeContactAdd,

UIButtonTypePlain API_AVAILABLE(tvos(11.0)) __IOS_PROHIBITED __WATCHOS_PROHIBITED, // standard system button without the blurred background view

UIButtonTypeRoundedRect = UIButtonTypeSystem  // Deprecated, use UIButtonTypeSystem instead

};

NS_CLASS_AVAILABLE_IOS(2_0) @interface UIButton : UIControl+ (instancetype)buttonWithType:(UIButtonType)buttonType;//创建button

@property(nonatomic)          UIEdgeInsets contentEdgeInsets UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsZero. On tvOS 10 or later, default is nonzero except for custom buttons.调整button整体内部区域的位置和尺寸

@property(nonatomic)          UIEdgeInsets titleEdgeInsets;                // default is UIEdgeInsetsZero--调整button文字区域的位置和尺寸

@property(nonatomic)          BOOL        reversesTitleShadowWhenHighlighted; // default is NO. if YES, shadow reverses to shift between engrave and emboss appearance

@property(nonatomic)          UIEdgeInsets imageEdgeInsets;                // default is UIEdgeInsetsZero-----调整button图片区域的位置和尺寸

@property(nonatomic)          BOOL        adjustsImageWhenHighlighted;    // default is YES. if YES, image is drawn darker when highlighted(pressed)

@property(nonatomic)          BOOL        adjustsImageWhenDisabled;      // default is YES. if YES, image is drawn lighter when disabled

@property(nonatomic)          BOOL        showsTouchWhenHighlighted __TVOS_PROHIBITED;      // default is NO. if YES, show a simple feedback (currently a glow) while highlighted

@property(null_resettable, nonatomic,strong)  UIColor    *tintColor NS_AVAILABLE_IOS(5_0); // The tintColor is inherited through the superview hierarchy. See UIView for more information.

@property(nonatomic,readonly) UIButtonType buttonType;

// you can set the image, title color, title shadow color, and background image to use for each state. you can specify data

// for a combined state by using the flags added together. in general, you should specify a value for the normal state to be used

// by other states which don't have a custom value set

- (void)setTitle:(nullable NSString *)title forState:(UIControlState)state;                    // default is nil. title is assumed to be single line

- (void)setTitleColor:(nullable UIColor *)color forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default if nil. use opaque white

- (void)setTitleShadowColor:(nullable UIColor *)color forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default is nil. use 50% black

- (void)setImage:(nullable UIImage *)image forState:(UIControlState)state;                      // default is nil. should be same size if different for different states

- (void)setBackgroundImage:(nullable UIImage *)image forState:(UIControlState)state UI_APPEARANCE_SELECTOR; // default is nil

- (void)setAttributedTitle:(nullable NSAttributedString *)title forState:(UIControlState)state NS_AVAILABLE_IOS(6_0); // default is nil. title is assumed to be single line

- (nullable NSString *)titleForState:(UIControlState)state;          // these getters only take a single state value

- (nullable UIColor *)titleColorForState:(UIControlState)state;

- (nullable UIColor *)titleShadowColorForState:(UIControlState)state;

- (nullable UIImage *)imageForState:(UIControlState)state;

- (nullable UIImage *)backgroundImageForState:(UIControlState)state;

- (nullable NSAttributedString *)attributedTitleForState:(UIControlState)state NS_AVAILABLE_IOS(6_0);

// these are the values that will be used for the current state. you can also use these for overrides. a heuristic will be used to

// determine what image to choose based on the explict states set. For example, the 'normal' state value will be used for all states

// that don't have their own image defined.

@property(nullable, nonatomic,readonly,strong) NSString *currentTitle;            // normal/highlighted/selected/disabled. can return nil

@property(nonatomic,readonly,strong) UIColor  *currentTitleColor;        // normal/highlighted/selected/disabled. always returns non-nil. default is white(1,1)

@property(nullable, nonatomic,readonly,strong) UIColor  *currentTitleShadowColor;  // normal/highlighted/selected/disabled.

@property(nullable, nonatomic,readonly,strong) UIImage  *currentImage;            // normal/highlighted/selected/disabled. can return nil

@property(nullable, nonatomic,readonly,strong) UIImage  *currentBackgroundImage;  // normal/highlighted/selected/disabled. can return nil

@property(nullable, nonatomic,readonly,strong) NSAttributedString *currentAttributedTitle NS_AVAILABLE_IOS(6_0);  // normal/highlighted/selected/disabled. can return nil

// return title and image views. will always create them if necessary. always returns nil for system buttons

@property(nullable, nonatomic,readonly,strong) UILabel    *titleLabel NS_AVAILABLE_IOS(3_0);

@property(nullable, nonatomic,readonly,strong) UIImageView *imageView  NS_AVAILABLE_IOS(3_0);

/ these return the rectangle for the background (assumes bounds), the content (image + title) and for the image and title separately. the content rect is calculated based

// on the title and image size and padding and then adjusted based on the control content alignment. there are no draw methods since the contents

// are rendered in separate subviews (UIImageView, UILabel)

- (CGRect)backgroundRectForBounds:(CGRect)bounds;

- (CGRect)contentRectForBounds:(CGRect)bounds;// 用来计算按钮整体内容区域的大小和位置

- (CGRect)titleRectForContentRect:(CGRect)contentRect;// 用来计算按钮文字区域的大小和位置

- (CGRect)imageRectForContentRect:(CGRect)contentRect;// 用来计算按钮图片区域的大小和位置

@end

@interface UIButton(UIButtonDeprecated)

@property(nonatomic,strong) UIFont *font NS_DEPRECATED_IOS(2_0, 3_0) __TVOS_PROHIBITED;

@property(nonatomic) NSLineBreakMode lineBreakMode NS_DEPRECATED_IOS(2_0, 3_0) __TVOS_PROHIBITED;

@property(nonatomic) CGSize titleShadowOffset NS_DEPRECATED_IOS(2_0, 3_0) __TVOS_PROHIBITED;

@end#if TARGET_OS_IOS

@interface UIButton (SpringLoading)

@end

#endif

NS_ASSUME_NONNULL_END


注意:默认情况下,Cocoa Touch 框架中的UIButton只支持左图右文的布局方式,而且还不能直接设置图文间距。


但是在现实开发中,我们会遇到要求--上图片下文字;右图左文;上文下图的情况。

针对这种情况,我们的通常思路就是创建UIButton的子类或者类别

UIButton的类别


#import<UIKit/UIKit.h>

//系统默认的布局是内容整体居中,图片在左,文字在右,图片和文字间距为 0。

typedef NS_ENUM(NSInteger,ZBButtonLayoutStyle) {

ZBButtonLayoutTitleLeft,

ZBButtonLayoutTitleRight,

ZBButtonLayoutTitleTop,

ZBButtonLayoutTitleBottom,

};

@interface UIButton (TitleAndImageLayout)

- (void)zb_setLayoutStyle:(ZBButtonLayoutStyle)style spacing:(CGFloat)spacing;

@end


#import "UIButton+TitleAndImageLayout.h"

@implementation UIButton (TitleAndImageLayout)

- (void)zb_setLayoutStyle:(ZBButtonLayoutStyle)style spacing:(CGFloat)spacing{

[self layoutIfNeeded];//为了获取最新的 imageView 和 titleLabel 的 frame,强制更新

CGRect titleFrame = self.titleLabel.frame;

CGRect imageFrame = self.imageView.frame;

switch (style) {

//  UIEdgeInsets 类型有四个成员变量 top、left、bottom、right,分别表示上左下右四个方向的偏移量,正值代表往内缩进,也就是往按钮中心靠拢,负值代表往外扩张,就是往按钮边缘贴近。

case ZBButtonLayoutTitleLeft:

{

// 图片右移

self.imageEdgeInsets = UIEdgeInsetsMake(0,

titleFrame.size.width + spacing,

0,

-(titleFrame.size.width + spacing));

// 文字左移

self.titleEdgeInsets = UIEdgeInsetsMake(0,

-(titleFrame.origin.x - imageFrame.origin.x),

0,

titleFrame.origin.x - imageFrame.origin.x);

}

break;

case ZBButtonLayoutTitleRight:

{

// 计算默认的图片文字间距

CGFloat originalSpacing = titleFrame.origin.x - (imageFrame.origin.x + imageFrame.size.width);

// 调整文字的位置

self.titleEdgeInsets = UIEdgeInsetsMake(0,

-(originalSpacing - spacing),

0,

(originalSpacing - spacing));

}

break;

case ZBButtonLayoutTitleTop:

{

// 图片下移,右移

self.imageEdgeInsets = UIEdgeInsetsMake(titleFrame.size.height + spacing,

0,

0,

-(titleFrame.size.width));

// 文字上移,左移

self.titleEdgeInsets = UIEdgeInsetsMake(0,

-(imageFrame.size.width),

imageFrame.size.height + spacing,

0);

}

break;

case ZBButtonLayoutTitleBottom:

{

// 图片上移,右移

self.imageEdgeInsets = UIEdgeInsetsMake(0,

0,

titleFrame.size.height + spacing,

-(titleFrame.size.width));

// 文字下移,左移

self.titleEdgeInsets = UIEdgeInsetsMake(imageFrame.size.height + spacing,

-(imageFrame.size.width),

0,

0);

}

break;

default:

break;

}

}

@end

使用方法

//button的大小会自动获取图片的大小,字体大小默认17

UIButton* left = [UIButton buttonWithType:UIButtonTypeCustom];

left.backgroundColor = [UIColor yellowColor];

[self.view addSubview:left];

left.frame = CGRectMake(50, 80, 50, 80);

[left setImage:[UIImage imageNamed:@"icon_交易方式@2x"] forState:UIControlStateNormal];

[left setTitle:@"左" forState:UIControlStateNormal];

left.titleLabel.font = [UIFont systemFontOfSize:36];

[left setTitleColor:[UIColor redColor] forState:UIControlStateNormal];

[left zb_setLayoutStyle:ZBButtonLayoutTitleLeft spacing:2];

创建UIButton的子类

#import<UIKit/UIKit.h>

typedef NS_ENUM(NSInteger,ZBButtonLayoutStyle) {

ZBButtonLayoutTitleLeft,

ZBButtonLayoutTitleRight,

ZBButtonLayoutTitleTop,

ZBButtonLayoutTitleBottom,

};

@interface UILayoutButton : UIButton

@property (assign,nonatomic)IBInspectable CGSize imageSize;//图片大小

@property (nonatomic,assign)CGFloat titleAndImageSpace;//文字和图片间的距离

@property (assign, nonatomic) ZBButtonLayoutStyle layoutStyle;    ///< 图片和文字的相对位置

@end


--------------------

#import "UILayoutButton.h"

@implementation UILayoutButton

/*

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

// Drawing code

}

*/

- (void)setLayoutStyle:(ZBButtonLayoutStyle)layoutStyle{

_layoutStyle = layoutStyle;

if (layoutStyle!= ZBButtonLayoutTitleRight&&layoutStyle!= ZBButtonLayoutTitleLeft) {

self.titleLabel.textAlignment = NSTextAlignmentCenter;

}

}

#pragma mark---重写父类方法,改变title和image的坐标

- (CGRect)imageRectForContentRect:(CGRect)contentRect{

if (self.layoutStyle == ZBButtonLayoutTitleLeft) {

CGFloat x = contentRect.size.width - self.titleAndImageSpace - self.imageSize.width ;

CGFloat y =  contentRect.size.height -  self.imageSize.height;

y = y/2;

CGRect rect = CGRectMake(x,y,self.imageSize.width,self.imageSize.height);

return rect;

} else if (self.layoutStyle == ZBButtonLayoutTitleBottom) {

CGFloat x =  contentRect.size.width -  self.imageSize.width;

CGFloat  y=  0  ;

x = x / 2;

CGRect rect = CGRectMake(x,y,self.imageSize.width,self.imageSize.height);

return rect;

}else if (self.layoutStyle == ZBButtonLayoutTitleTop){

CGFloat x =  contentRect.size.width -  self.imageSize.width;

CGFloat  y=  contentRect.size.height-self.titleAndImageSpace-self.imageSize.height  ;

x = x / 2;

CGRect rect = CGRectMake(x,y,self.imageSize.width,self.imageSize.height);

return rect;

}

else {

return [super imageRectForContentRect:contentRect];

}

}

- (CGRect)titleRectForContentRect:(CGRect)contentRect {

if (self.layoutStyle == ZBButtonLayoutTitleLeft) {

return CGRectMake(0, 0, contentRect.size.width - self.titleAndImageSpace - self.imageSize.width , contentRect.size.height);

} else if (self.layoutStyle == ZBButtonLayoutTitleBottom) {

return CGRectMake(0,  self.titleAndImageSpace + self.imageSize.height , contentRect.size.width , contentRect.size.height - self.titleAndImageSpace - self.imageSize.height );

}

else if (self.layoutStyle == ZBButtonLayoutTitleTop){

return CGRectMake((contentRect.size.width- self.imageSize.width)/2.0 ,  0 , contentRect.size.width , contentRect.size.height - self.titleAndImageSpace - self.imageSize.height );

}

else {

return [super titleRectForContentRect:contentRect];

}

}

@end

使用方法:


UILayoutButton *left = [UILayoutButton buttonWithType:UIButtonTypeCustom];

left.backgroundColor = [UIColor redColor];

left.titleLabel.backgroundColor = [UIColor grayColor];

left.imageView.backgroundColor = [UIColor purpleColor];

left.frame = CGRectMake(50, 160, 110, 30);

[left setTitle:@"标题在左" forState:UIControlStateNormal];

left.imageSize = CGSizeMake(20, 20);//设置图片大小

left.layoutStyle = ZBButtonLayoutTitleLeft;//布局风格

[left setImage:[UIImage imageNamed:@"check_icon"] forState:UIControlStateNormal];

[self.view addSubview:left];

总结:

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

各位,可以把您的看法留在下方评论区。如有不足,也请指教,我这里就抱砖引玉了。

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

推荐阅读更多精彩内容