iOS开发基础5-UIButton:高阶用法与封装工具类
深度解析 UIButton:高阶用法与封装工具类
UIButton 是 iOS 开发中最常用的交互控件之一,继承自 UIControl,用于响应用户触摸操作。它支持多种状态样式、图文混排和事件处理。本文从基础用法出发,逐步深入高阶技巧、工具类封装与底层实现。
一、基础使用
1.1 按钮类型
UIButton 通过 buttonWithType: 类方法创建,支持以下类型:
| 类型 | 说明 |
|---|---|
UIButtonTypeCustom |
自定义样式,无默认外观,标题颜色默认为白色 |
UIButtonTypeSystem |
系统按钮,默认蓝色文字,按下时有透明度变化(iOS 7+) |
UIButtonTypeDetailDisclosure |
详情披露按钮,显示蓝色 "i" 图标 |
UIButtonTypeInfoLight |
浅色背景的信息按钮 |
UIButtonTypeInfoDark |
深色背景的信息按钮 |
UIButtonTypeContactAdd |
添加联系人按钮,显示蓝色 "+" 图标 |
UIButtonTypeClose |
关闭按钮,显示灰色 "x" 图标(iOS 13+) |
1.2 创建按钮
Objective-C
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
button.frame = CGRectMake(50, 100, 200, 50);
[button setTitle:@"Click Me" forState:UIControlStateNormal];
[self.view addSubview:button];
Swift
let button = UIButton(type: .system)
button.frame = CGRect(x: 50, y: 100, width: 200, height: 50)
button.setTitle("Click Me", for: .normal)
view.addSubview(button)
1.3 状态与样式设置
UIButton 支持四种核心状态,可分别设置标题、标题颜色、图片和背景图片:
| 状态 | 说明 |
|---|---|
UIControlStateNormal |
正常状态(默认) |
UIControlStateHighlighted |
按下高亮状态 |
UIControlStateDisabled |
禁用状态(enabled = NO) |
UIControlStateSelected |
选中状态(selected = YES) |
Objective-C
// 标题
[button setTitle:@"Normal" forState:UIControlStateNormal];
[button setTitle:@"Highlighted" forState:UIControlStateHighlighted];
// 标题颜色
[button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
// 图片(按钮内部图标,与标题并列显示)
[button setImage:[UIImage imageNamed:@"icon_normal"] forState:UIControlStateNormal];
[button setImage:[UIImage imageNamed:@"icon_highlighted"] forState:UIControlStateHighlighted];
// 背景图片(铺满整个按钮,标题显示在其上)
[button setBackgroundImage:[UIImage imageNamed:@"bg_normal"] forState:UIControlStateNormal];
[button setBackgroundImage:[UIImage imageNamed:@"bg_highlighted"] forState:UIControlStateHighlighted];
Swift
button.setTitle("Normal", for: .normal)
button.setTitle("Highlighted", for: .highlighted)
button.setTitleColor(.blue, for: .normal)
button.setTitleColor(.red, for: .highlighted)
button.setImage(UIImage(named: "icon_normal"), for: .normal)
button.setImage(UIImage(named: "icon_highlighted"), for: .highlighted)
button.setBackgroundImage(UIImage(named: "bg_normal"), for: .normal)
button.setBackgroundImage(UIImage(named: "bg_highlighted"), for: .highlighted)
image与backgroundImage的区别:image是按钮内部图标,与titleLabel并列排布;backgroundImage是背景图,铺满整个按钮,标题和图标显示在其上方。
二、高阶用法
2.1 自定义样式(圆角与边框)
通过 layer 属性可以实现圆角、边框等自定义外观。
Objective-C
UIButton *customButton = [UIButton buttonWithType:UIButtonTypeCustom];
customButton.frame = CGRectMake(50, 200, 200, 50);
[customButton setTitle:@"Custom Button" forState:UIControlStateNormal];
[customButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
customButton.backgroundColor = [UIColor purpleColor];
// 圆角:纯背景色 + 圆角不需要 masksToBounds,避免离屏渲染
customButton.layer.cornerRadius = 10;
// 边框
customButton.layer.borderColor = [UIColor whiteColor].CGColor;
customButton.layer.borderWidth = 2.0;
// 仅当有子视图/图片需要裁剪到圆角内时才开启
// customButton.layer.masksToBounds = YES;
[self.view addSubview:customButton];
Swift
let customButton = UIButton(type: .custom)
customButton.frame = CGRect(x: 50, y: 200, width: 200, height: 50)
customButton.setTitle("Custom Button", for: .normal)
customButton.setTitleColor(.white, for: .normal)
customButton.backgroundColor = .purple
customButton.layer.cornerRadius = 10
customButton.layer.borderColor = UIColor.white.cgColor
customButton.layer.borderWidth = 2.0
// 仅在需要裁剪溢出内容时开启
// customButton.layer.masksToBounds = true
view.addSubview(customButton)
性能提示:
cornerRadius配合backgroundColor可以直接显示圆角,无需设置masksToBounds = YES。masksToBounds会触发离屏渲染(Off-screen Rendering),在列表中大量使用时会造成卡顿。只有当按钮内部有图片或子视图需要被圆角裁剪时,才需要开启。
2.2 富文本标题
通过 NSAttributedString 可以在按钮标题中混合不同字体、颜色和样式。
Objective-C
NSString *text = @"Rich Text Button";
NSMutableAttributedString *attributedTitle = [[NSMutableAttributedString alloc] initWithString:text];
// "Rich" 部分:黑色 + 粗体
[attributedTitle addAttribute:NSForegroundColorAttributeName
value:[UIColor blackColor]
range:NSMakeRange(0, 4)];
[attributedTitle addAttribute:NSFontAttributeName
value:[UIFont boldSystemFontOfSize:18]
range:NSMakeRange(0, 4)];
// "Text" 部分:红色 + 斜体
[attributedTitle addAttribute:NSForegroundColorAttributeName
value:[UIColor redColor]
range:NSMakeRange(5, 4)];
[attributedTitle addAttribute:NSFontAttributeName
value:[UIFont italicSystemFontOfSize:18]
range:NSMakeRange(5, 4)];
[customButton setAttributedTitle:attributedTitle forState:UIControlStateNormal];
Swift
let text = "Rich Text Button"
let attributedTitle = NSMutableAttributedString(string: text)
attributedTitle.addAttribute(.foregroundColor, value: UIColor.black, range: NSRange(location: 0, length: 4))
attributedTitle.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: 18), range: NSRange(location: 0, length: 4))
attributedTitle.addAttribute(.foregroundColor, value: UIColor.red, range: NSRange(location: 5, length: 4))
attributedTitle.addAttribute(.font, value: UIFont.italicSystemFont(ofSize: 18), range: NSRange(location: 5, length: 4))
customButton.setAttributedTitle(attributedTitle, for: .normal)
2.3 图文混排按钮
按钮可以同时显示图标和文字,通过 imageEdgeInsets 和 titleEdgeInsets 调整相对位置。常见有四种布局:左图右文(默认)、右图左文、上图下文、下图上文。
左图右文(默认)
Objective-C
UIButton *imageButton = [UIButton buttonWithType:UIButtonTypeCustom];
imageButton.frame = CGRectMake(50, 300, 200, 50);
[imageButton setTitle:@"Icon Button" forState:UIControlStateNormal];
[imageButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[imageButton setImage:[UIImage imageNamed:@"icon"] forState:UIControlStateNormal];
// 图标左移 10pt,标题右移 10pt,增大间距
imageButton.imageEdgeInsets = UIEdgeInsetsMake(0, -10, 0, 0);
imageButton.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
[self.view addSubview:imageButton];
Swift
let imageButton = UIButton(type: .custom)
imageButton.frame = CGRect(x: 50, y: 300, width: 200, height: 50)
imageButton.setTitle("Icon Button", for: .normal)
imageButton.setTitleColor(.black, for: .normal)
imageButton.setImage(UIImage(named: "icon"), for: .normal)
imageButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: -10, bottom: 0, right: 0)
imageButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
view.addSubview(imageButton)
四种布局通用方法
通过封装一个方法,根据图片位置自动计算 imageEdgeInsets 和 titleEdgeInsets:
Objective-C(UIButton 分类)
// UIButton+ImagePosition.h
typedef NS_ENUM(NSInteger, ButtonImagePosition) {
ButtonImagePositionLeft, // 图片在左,文字在右(默认)
ButtonImagePositionRight, // 图片在右,文字在左
ButtonImagePositionTop, // 图片在上,文字在下
ButtonImagePositionBottom // 图片在下,文字在上
};
@interface UIButton (ImagePosition)
- (void)setImagePosition:(ButtonImagePosition)position spacing:(CGFloat)spacing;
@end
// UIButton+ImagePosition.m
@implementation UIButton (ImagePosition)
- (void)setImagePosition:(ButtonImagePosition)position spacing:(CGFloat)spacing {
[self layoutIfNeeded];
CGSize imageSize = self.imageView.frame.size;
CGSize titleSize = self.titleLabel.frame.size;
CGFloat imageOffsetX = (imageSize.width + titleSize.width / 2) - (imageSize.width + titleSize.width + spacing) / 2;
CGFloat imageOffsetY = imageSize.height / 2;
CGFloat titleOffsetX = (titleSize.width + imageSize.width / 2) - (imageSize.width + titleSize.width + spacing) / 2;
CGFloat titleOffsetY = titleSize.height / 2;
UIEdgeInsets imageEdge = UIEdgeInsetsZero;
UIEdgeInsets titleEdge = UIEdgeInsetsZero;
switch (position) {
case ButtonImagePositionLeft:
imageEdge = UIEdgeInsetsMake(0, -spacing/2, 0, spacing/2);
titleEdge = UIEdgeInsetsMake(0, spacing/2, 0, -spacing/2);
break;
case ButtonImagePositionRight:
imageEdge = UIEdgeInsetsMake(0, titleSize.width + spacing/2, 0, -(titleSize.width + spacing/2));
titleEdge = UIEdgeInsetsMake(0, -(imageSize.width + spacing/2), 0, imageSize.width + spacing/2);
break;
case ButtonImagePositionTop:
imageEdge = UIEdgeInsetsMake(-(titleSize.height + spacing/2), titleSize.width/2, titleSize.height + spacing/2, -titleSize.width/2);
titleEdge = UIEdgeInsetsMake(imageSize.height + spacing/2, -imageSize.width/2, -(imageSize.height + spacing/2), imageSize.width/2);
break;
case ButtonImagePositionBottom:
imageEdge = UIEdgeInsetsMake(titleSize.height + spacing/2, titleSize.width/2, -(titleSize.height + spacing/2), -titleSize.width/2);
titleEdge = UIEdgeInsetsMake(-(imageSize.height + spacing/2), -imageSize.width/2, imageSize.height + spacing/2, imageSize.width/2);
break;
}
self.imageEdgeInsets = imageEdge;
self.titleEdgeInsets = titleEdge;
}
@end
Swift(UIButton 扩展)
enum ButtonImagePosition {
case left, right, top, bottom
}
extension UIButton {
func setImagePosition(_ position: ButtonImagePosition, spacing: CGFloat) {
layoutIfNeeded()
guard let imageSize = imageView?.frame.size,
let titleSize = titleLabel?.frame.size else { return }
var imageEdge = UIEdgeInsets.zero
var titleEdge = UIEdgeInsets.zero
switch position {
case .left:
imageEdge = UIEdgeInsets(top: 0, left: -spacing/2, bottom: 0, right: spacing/2)
titleEdge = UIEdgeInsets(top: 0, left: spacing/2, bottom: 0, right: -spacing/2)
case .right:
imageEdge = UIEdgeInsets(top: 0, left: titleSize.width + spacing/2,
bottom: 0, right: -(titleSize.width + spacing/2))
titleEdge = UIEdgeInsets(top: 0, left: -(imageSize.width + spacing/2),
bottom: 0, right: imageSize.width + spacing/2)
case .top:
imageEdge = UIEdgeInsets(top: -(titleSize.height + spacing/2),
left: titleSize.width/2,
bottom: titleSize.height + spacing/2,
right: -titleSize.width/2)
titleEdge = UIEdgeInsets(top: imageSize.height + spacing/2,
left: -imageSize.width/2,
bottom: -(imageSize.height + spacing/2),
right: imageSize.width/2)
case .bottom:
imageEdge = UIEdgeInsets(top: titleSize.height + spacing/2,
left: titleSize.width/2,
bottom: -(titleSize.height + spacing/2),
right: -titleSize.width/2)
titleEdge = UIEdgeInsets(top: -(imageSize.height + spacing/2),
left: -imageSize.width/2,
bottom: imageSize.height + spacing/2,
right: imageSize.width/2)
}
imageEdgeInsets = imageEdge
titleEdgeInsets = titleEdge
}
}
使用示例:
[button setImagePosition:ButtonImagePositionTop spacing:8]; // 上图下文,间距 8pt
button.setImagePosition(.top, spacing: 8)
注意:基于
imageEdgeInsets/titleEdgeInsets的图文布局在使用 Auto Layout 时,可能影响intrinsicContentSize的计算。iOS 15+ 推荐使用UIButton.Configuration的imagePlacement属性,原生支持四种布局且与 Auto Layout 完美兼容。
2.4 其他实用属性
| 属性 | 说明 |
|---|---|
adjustsImageWhenHighlighted |
高亮时是否自动变暗图片(默认 YES) |
adjustsImageWhenDisabled |
禁用时是否自动变亮图片(默认 YES) |
showsTouchWhenHighlighted |
高亮时是否显示发光效果(默认 NO) |
tintColor |
按钮的渲染颜色,对 system 类型按钮和模板图片生效 |
contentEdgeInsets |
内容(图片+标题整体)的内边距 |
2.5 iOS 15+:UIButton.Configuration
iOS 15 引入了声明式按钮配置 API,统一管理外观、布局和交互,推荐新项目优先使用。
Objective-C
UIButtonConfiguration *config = [UIButtonConfiguration filledButtonConfiguration];
config.title = @"Filled Button";
config.subtitle = @"Subtitle"; // 副标题
config.image = [UIImage systemImageNamed:@"star"]; // SF Symbols 图标
config.imagePlacement = NSDirectionalRectEdgeTop; // 图标在上方
config.imagePadding = 8; // 图标与文字间距
config.baseBackgroundColor = [UIColor systemBlueColor];
config.baseForegroundColor = [UIColor whiteColor];
config.cornerStyle = UIButtonConfigurationCornerStyleLarge;
UIButton *button = [UIButton buttonWithConfiguration:config primaryAction:nil];
button.frame = CGRectMake(50, 100, 200, 60);
[self.view addSubview:button];
Swift
var config = UIButton.Configuration.filled()
config.title = "Filled Button"
config.subtitle = "Subtitle"
config.image = UIImage(systemName: "star")
config.imagePlacement = .top
config.imagePadding = 8
config.baseBackgroundColor = .systemBlue
config.baseForegroundColor = .white
config.cornerStyle = .large
let button = UIButton(configuration: config)
button.frame = CGRect(x: 50, y: 100, width: 200, height: 60)
view.addSubview(button)
Configuration 提供三种预设样式:
| 样式 | 说明 |
|---|---|
filled() |
填充式按钮,有背景色 |
tinted() |
浅色背景 + 深色文字 |
gray() |
灰色背景按钮 |
plain() |
无背景,仅文字/图标 |
三、事件处理
3.1 常见事件类型
UIControl 定义了多种触摸事件,最常用的是 UIControlEventTouchUpInside(在按钮范围内松开手指)。
| 事件 | 触发时机 |
|---|---|
touchDown |
手指按下 |
touchDownRepeat |
多次按下(双击及以上) |
touchUpInside |
在控件范围内松开(最常用) |
touchUpOutside |
在控件范围外松开 |
touchCancel |
触摸被系统中断(如来电) |
valueChanged |
控件值发生变化(如 UISwitch、UISlider) |
3.2 Target-Action 机制
按钮通过 addTarget:action:forControlEvents: 注册事件处理器。一个按钮可以注册多个 target-action,同一事件也可以关联多个方法。
Objective-C
[button addTarget:self
action:@selector(buttonClicked:)
forControlEvents:UIControlEventTouchUpInside];
- (void)buttonClicked:(UIButton *)sender {
NSLog(@"按钮被点击: %@", sender);
}
Swift
button.addTarget(self, action: #selector(buttonClicked(_:)), for: .touchUpInside)
@objc func buttonClicked(_ sender: UIButton) {
print("按钮被点击: \(sender)")
}
Swift 中
action对应的方法必须标记@objc,因为 Objective-C 运行时通过消息机制调用。
四、封装工具类
将常用按钮创建逻辑封装为工厂类,减少重复代码。
4.1 Objective-C 版本
ButtonFactory.h
#import <UIKit/UIKit.h>
@interface ButtonFactory : NSObject
/// 创建系统按钮
+ (UIButton *)systemButtonWithTitle:(NSString *)title
target:(id)target
action:(SEL)action
frame:(CGRect)frame;
/// 创建自定义圆角按钮
+ (UIButton *)customButtonWithTitle:(NSString *)title
titleColor:(UIColor *)titleColor
backgroundColor:(UIColor *)bgColor
cornerRadius:(CGFloat)cornerRadius
target:(id)target
action:(SEL)action
frame:(CGRect)frame;
/// 创建富文本按钮
+ (UIButton *)richTextButtonWithAttributedTitle:(NSAttributedString *)title
backgroundColor:(UIColor *)bgColor
target:(id)target
action:(SEL)action
frame:(CGRect)frame;
/// 创建图文按钮
+ (UIButton *)imageButtonWithTitle:(NSString *)title
titleColor:(UIColor *)titleColor
image:(UIImage *)image
position:(ButtonImagePosition)position
spacing:(CGFloat)spacing
target:(id)target
action:(SEL)action
frame:(CGRect)frame;
@end
ButtonFactory.m
#import "ButtonFactory.h"
#import "UIButton+ImagePosition.h"
@implementation ButtonFactory
+ (UIButton *)systemButtonWithTitle:(NSString *)title
target:(id)target
action:(SEL)action
frame:(CGRect)frame {
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
button.frame = frame;
[button setTitle:title forState:UIControlStateNormal];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
return button;
}
+ (UIButton *)customButtonWithTitle:(NSString *)title
titleColor:(UIColor *)titleColor
backgroundColor:(UIColor *)bgColor
cornerRadius:(CGFloat)cornerRadius
target:(id)target
action:(SEL)action
frame:(CGRect)frame {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = frame;
[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:titleColor forState:UIControlStateNormal];
button.backgroundColor = bgColor;
button.layer.cornerRadius = cornerRadius;
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
return button;
}
+ (UIButton *)richTextButtonWithAttributedTitle:(NSAttributedString *)title
backgroundColor:(UIColor *)bgColor
target:(id)target
action:(SEL)action
frame:(CGRect)frame {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = frame;
[button setAttributedTitle:title forState:UIControlStateNormal];
button.backgroundColor = bgColor;
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
return button;
}
+ (UIButton *)imageButtonWithTitle:(NSString *)title
titleColor:(UIColor *)titleColor
image:(UIImage *)image
position:(ButtonImagePosition)position
spacing:(CGFloat)spacing
target:(id)target
action:(SEL)action
frame:(CGRect)frame {
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = frame;
[button setTitle:title forState:UIControlStateNormal];
[button setTitleColor:titleColor forState:UIControlStateNormal];
[button setImage:image forState:UIControlStateNormal];
[button setImagePosition:position spacing:spacing];
[button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
return button;
}
@end
使用示例
UIButton *systemBtn = [ButtonFactory systemButtonWithTitle:@"系统按钮"
target:self
action:@selector(buttonAction)
frame:CGRectMake(50, 400, 200, 50)];
[self.view addSubview:systemBtn];
UIButton *customBtn = [ButtonFactory customButtonWithTitle:@"自定义按钮"
titleColor:[UIColor whiteColor]
backgroundColor:[UIColor purpleColor]
cornerRadius:10
target:self
action:@selector(buttonAction)
frame:CGRectMake(50, 470, 200, 50)];
[self.view addSubview:customBtn];
4.2 Swift 版本
Swift 中更适合使用 extension 扩展 UIButton,配合默认参数实现便捷创建:
extension UIButton {
static func systemButton(title: String,
target: Any?,
action: Selector,
frame: CGRect = .zero) -> UIButton {
let btn = UIButton(type: .system)
btn.frame = frame
btn.setTitle(title, for: .normal)
btn.addTarget(target, action: action, for: .touchUpInside)
return btn
}
static func customButton(title: String,
titleColor: UIColor = .white,
backgroundColor: UIColor,
cornerRadius: CGFloat = 8,
target: Any?,
action: Selector,
frame: CGRect = .zero) -> UIButton {
let btn = UIButton(type: .custom)
btn.frame = frame
btn.setTitle(title, for: .normal)
btn.setTitleColor(titleColor, for: .normal)
btn.backgroundColor = backgroundColor
btn.layer.cornerRadius = cornerRadius
btn.addTarget(target, action: action, for: .touchUpInside)
return btn
}
}
使用示例
let systemBtn = UIButton.systemButton(title: "系统按钮",
target: self,
action: #selector(buttonAction),
frame: CGRect(x: 50, y: 400, width: 200, height: 50))
view.addSubview(systemBtn)
let customBtn = UIButton.customButton(title: "自定义按钮",
backgroundColor: .purple,
target: self,
action: #selector(buttonAction),
frame: CGRect(x: 50, y: 470, width: 200, height: 50))
view.addSubview(customBtn)
五、底层实现逻辑
5.1 继承关系与事件传递
UIResponder
└── UIView
└── UIControl
└── UIButton
UIView提供基础的渲染、触摸事件接收和层级管理。UIControl在UIView基础上实现了 Target-Action 机制,将原始触摸事件(touchesBegan/touchesMoved/touchesEnded)转换为语义化的控件事件(touchUpInside等)。UIButton进一步封装了状态管理和多内容(标题/图片/背景图)切换。
5.2 渲染与布局
UIButton 内部包含两个子视图:
titleLabel(UILabel类型):显示按钮标题。imageView(UIImageView类型):显示按钮图标。
按钮在 layoutSubviews 中根据 contentEdgeInsets、imageEdgeInsets、titleEdgeInsets 和当前状态,计算并布局这两个子视图的位置。背景图则直接绘制在按钮自身的 layer 上。
5.3 状态管理
按钮维护一个 state 属性(UIControlState 类型的位掩码),在以下时机更新状态:
- 手指按下 → 进入
Highlighted状态。 - 手指抬起 → 回到
Normal状态(或保持Selected)。 enabled = NO→ 进入Disabled状态。selected = YES→ 叠加Selected状态。
状态变化时,按钮调用 setNeedsLayout,从内部存储中取出当前状态对应的标题、图片、背景图和标题颜色,更新 titleLabel 和 imageView。不同状态的内容分别存储,互不干扰。
5.4 性能优化
- 预渲染与缓存:不同状态的标题和图片在设置时就准备好,状态切换时直接赋值,避免实时计算。
- 按需重绘:仅在状态变化时更新子视图内容,普通状态下不触发
drawRect:。 UIButtonTypeSystem类型的高亮效果通过改变alpha实现,不需要重新渲染图片。
六、总结
UIButton继承自UIControl,通过 Target-Action 机制处理交互,支持 Normal / Highlighted / Disabled / Selected 四种状态。- 自定义样式时注意
masksToBounds的性能影响,纯背景色圆角不需要开启。 - 图文混排可通过
imageEdgeInsets/titleEdgeInsets实现四种布局;iOS 15+ 推荐使用UIButton.Configuration的imagePlacement。 - 封装工厂类或扩展可以减少重复代码,提升开发效率。
- 理解内部
titleLabel+imageView的双子视图结构和状态管理机制,有助于排查布局和样式问题。

浙公网安备 33010602011771号