如何实现含Label和Button的Custom View并适配多ViewController不同点击事件?
这是iOS开发里非常常见的自定义View复用场景,我平时做项目的时候常用下面这几种方案,你可以根据需求选择:
1. 闭包(Closure)方式(最简洁,推荐简单场景使用)
这种方式代码量少,逻辑直接,很适合只需要处理单一点击事件的情况。
实现步骤:
- 在自定义View里定义一个可选的闭包属性,用来承载外部传入的点击逻辑
- 给Button添加点击事件,在事件方法里调用这个闭包
自定义View代码示例:
import UIKit class CustomActionView: UIView { // 自定义Label private let titleLabel: UILabel = { let label = UILabel() label.text = "这是自定义Label" label.textAlignment = .center label.translatesAutoresizingMaskIntoConstraints = false return label }() // 自定义Button private let actionButton: UIButton = { let button = UIButton(type: .system) button.setTitle("点击我", for: .normal) button.translatesAutoresizingMaskIntoConstraints = false return button }() // 定义点击回调闭包 var onButtonTap: (() -> Void)? override init(frame: CGRect) { super.init(frame: frame) setupUI() } required init?(coder: NSCoder) { super.init(coder: coder) setupUI() } private func setupUI() { backgroundColor = .lightGray addSubview(titleLabel) addSubview(actionButton) // 添加约束 NSLayoutConstraint.activate([ titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor), titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 20), actionButton.centerXAnchor.constraint(equalTo: centerXAnchor), actionButton.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 20), actionButton.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20) ]) // 绑定Button点击事件 actionButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside) } @objc private func buttonTapped() { // 调用外部传入的闭包 onButtonTap?() } }
在ViewController中使用:
class FirstViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let customView = CustomActionView(frame: CGRect(x: 50, y: 200, width: view.bounds.width - 100, height: 120)) // 给闭包赋值,写当前VC的点击逻辑 customView.onButtonTap = { print("第一个VC里的按钮被点击了!") // 这里可以写具体的业务逻辑,比如弹出弹窗、跳转页面等 } view.addSubview(customView) } } class SecondViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let customView = CustomActionView(frame: CGRect(x: 50, y: 200, width: view.bounds.width - 100, height: 120)) customView.onButtonTap = { print("第二个VC里的按钮被点击了!") // 这里写第二个VC独有的点击逻辑 } view.addSubview(customView) } }
2. 代理(Delegate)模式(适合多交互场景)
如果你的自定义View后续可能需要添加更多的回调事件(比如Label的点击、状态变化通知等),代理模式会让代码结构更清晰,扩展性更好。
实现步骤:
- 定义一个代理协议,声明需要的回调方法
- 在自定义View里添加代理属性
- Button点击时调用代理的对应方法
自定义View代码示例:
import UIKit // 定义代理协议 protocol CustomActionViewDelegate: AnyObject { func customActionViewDidTapButton(_ view: CustomActionView) } class CustomActionView: UIView { // 代理属性,用weak避免循环引用 weak var delegate: CustomActionViewDelegate? // 下面的Label、Button、UI布局代码和闭包方式一致,省略重复部分... @objc private func buttonTapped() { // 调用代理方法 delegate?.customActionViewDidTapButton(self) } }
在ViewController中使用:
class FirstViewController: UIViewController, CustomActionViewDelegate { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let customView = CustomActionView(frame: CGRect(x: 50, y: 200, width: view.bounds.width - 100, height: 120)) customView.delegate = self view.addSubview(customView) } // 实现代理方法 func customActionViewDidTapButton(_ view: CustomActionView) { print("第一个VC通过代理收到按钮点击事件!") } }
3. Target-Action 方式(贴近系统原生用法)
这种方式和UIButton本身的使用逻辑一致,允许外部直接给Button绑定target和action,灵活性很高。
实现步骤:
- 在自定义View里暴露Button的属性(或者提供一个方法来设置target-action)
- 外部ViewController直接给Button添加点击事件
自定义View代码示例:
import UIKit class CustomActionView: UIView { // 把Button设为公开属性,或者用private+方法来设置,这里用公开属性示例 let actionButton: UIButton = { let button = UIButton(type: .system) button.setTitle("点击我", for: .normal) button.translatesAutoresizingMaskIntoConstraints = false return button }() // 其他Label、布局代码省略... }
在ViewController中使用:
class FirstViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .white let customView = CustomActionView(frame: CGRect(x: 50, y: 200, width: view.bounds.width - 100, height: 120)) // 直接给Button添加点击事件 customView.actionButton.addTarget(self, action: #selector(customButtonTapped), for: .touchUpInside) view.addSubview(customView) } @objc private func customButtonTapped() { print("第一个VC通过Target-Action收到按钮点击!") } }
方案总结
- 闭包:代码简洁,适合单一回调场景,推荐优先使用
- 代理:结构清晰,适合多回调、复杂交互的场景,扩展性强
- Target-Action:贴近系统原生用法,灵活度高,但如果自定义View需要对Button点击做统一预处理的话不太方便
内容的提问来源于stack exchange,提问作者arinjay
相关产品推荐
相关产品推荐

