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

Swift MVC模式下自定义UIView内按钮如何绑定ViewController中的点击方法

问题原因

你报错的根本原因有两个:

  • MyView内部没有持有MyViewController的实例对象,你直接传MyViewController类作为target是无效的,addTarget需要的是方法所属的实例对象
  • showDeleteAlert是MyViewController的实例方法,作用域仅在MyViewController内部,MyView默认无法直接访问
可行实现方案

三种实现方案均符合MVC分层规范,可根据你的项目复杂度选择:

方案1:在ViewController中给按钮添加target(最简便)

不需要修改MyView的核心逻辑,只需要把addTarget的逻辑移到VC层即可,符合MVC的职责划分:VC负责处理交互事件,View只负责视图渲染。
修改后的MyView代码:

class MyView: UIView {
    // 删除原来错误的addTarget行,按钮只做自身样式配置
    lazy var deleteButton: UIButton = {
        let button = UIButton()
        button.setTitle("delete", for: .normal)
        return button
    }()

// 其余代码保持不变
}

修改后的MyViewController的viewDidLoad代码:

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(myView)
    myView.frame = CGRect(origin: .zero, size: view.bounds.size)
    // 新增这行,在VC层给按钮绑定点击事件
    myView.deleteButton.addTarget(self, action: #selector(showDeleteAlert), for: .touchUpInside)
    // 其余代码保持不变
}

方案2:闭包回调(解耦性更强)

适合View的交互事件较多、需要统一管理的场景:
修改后的MyView代码:

class MyView: UIView {
    // 定义点击回调闭包
    var deleteAction: (() -> Void)?
    
    lazy var deleteButton: UIButton = {
        let button = UIButton()
        button.setTitle("delete", for: .normal)
        // target设为View自身,绑定View内部的方法
        button.addTarget(self, action: #selector(deleteButtonClicked), for: .touchUpInside)
        return button
    }()

    @objc private func deleteButtonClicked() {
        // 触发回调
        deleteAction?()
    }
// 其余代码保持不变
}

修改后的MyViewController的viewDidLoad代码:

override func viewDidLoad() {
    super.viewDidLoad()
    view.addSubview(myView)
    myView.frame = CGRect(origin: .zero, size: view.bounds.size)
    // 给闭包赋值,处理点击逻辑,[weak self]避免循环引用
    myView.deleteAction = { [weak self] in
        self?.showDeleteAlert()
    }
    // 其余代码保持不变
}

方案3:代理模式

适合交互逻辑复杂、多个事件需要统一协议约束的场景:
修改后的MyView代码:

// 定义代理协议
protocol MyViewDelegate: AnyObject {
    func didClickDeleteButton()
}

class MyView: UIView {
    // 定义弱引用代理属性,避免循环引用
    weak var delegate: MyViewDelegate?
    
    lazy var deleteButton: UIButton = {
        let button = UIButton()
        button.setTitle("delete", for: .normal)
        button.addTarget(self, action: #selector(deleteButtonClicked), for: .touchUpInside)
        return button
    }()

    @objc private func deleteButtonClicked() {
        delegate?.didClickDeleteButton()
    }
// 其余代码保持不变
}

修改后的MyViewController代码:

// 遵守MyViewDelegate协议
class MyViewController: UIViewController, MyViewDelegate {
    private var myView = MyView()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubview(myView)
        myView.frame = CGRect(origin: .zero, size: view.bounds.size)
        // 设置代理为当前VC
        myView.delegate = self
        // 其余代码保持不变
    }

    @objc func showDeleteAlert() {
        print("showDelete is pressed")
    }

    // 实现代理方法
    func didClickDeleteButton() {
        showDeleteAlert()
    }
}

内容的提问来源于stack exchange,提问作者Yuuu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.10 03:15:02