@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的子控件的效果,各有优缺点:
各位,可以把您的看法留在下方评论区。如有不足,也请指教,我这里就抱砖引玉了。