You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现含Label和Button的Custom View并适配多ViewController不同点击事件?

解决自定义View中Button点击事件随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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:14:41