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

自定义HeaderView(UIView)内按钮添加Target无响应问题求助

自定义HeaderView内按钮Target不生效问题排查

我给ViewController实现了自定义HeaderView(UIView),但该UIView内的按钮添加的Target无法生效,调试时发现对应事件代码未执行。

相关代码

ViewController的loadView()代码

let headerViewModel = HeaderViewModel(title: "Farmers", helpAction:  {self.navigationController?.popViewController(animated: true) // 调试时该行代码未执行 })
let headerView = HeaderView(viewModel: headerViewModel)

HeaderView实现代码

import UIKit

protocol HeaderViewModelProtocol {
    var title: String { get }
    var color: UIColor { get }
    var imageName: String? { get }
    var backAction: (()->Void)? { get set}
    var helpAction: (()->Void)? { get set}
}

struct HeaderViewModel: HeaderViewModelProtocol {
    let title: String
    var color: UIColor = UIColor.white
    var imageName: String?
    var backAction: (() -> Void)?
    var helpAction: (() -> Void)?
}

class HeaderView: UIView {
    
    let viewModel: HeaderViewModelProtocol
    private var layer0: CAGradientLayer!
    
    var backButton: UIButton = {
        let button = UIButton()
            button.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
            button.addTarget(HeaderView.self, action: #selector(tapBack), for: .touchUpInside)
            button.backgroundColor = UIColor.red
            button.tag=5
            button.translatesAutoresizingMaskIntoConstraints = false
            button.backgroundColor = .clear
            button.tintColor = .white
            button.setImage(UIImage(named: "back"), for: .normal)
            return button
    }()
    
    
    var helpButton: UIButton = {
        let button = UIButton()
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = .clear
        button.setImage(UIImage(named: "plus"), for: .normal)
        button.tintColor = .white
        return button
    }()
    init(viewModel: HeaderViewModelProtocol) {
        self.viewModel = viewModel
        super.init(frame: .zero)
        loadView()
    }
    func loadView() {
        if viewModel.backAction != nil {
            backButton.addTarget(self, action: #selector(tapBack), for: .touchUpInside)
            
            addSubview(backButton)
            
            backButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 30).isActive = true
            backButton.topAnchor.constraint(equalTo: topAnchor, constant: 36).isActive = true
            backButton.widthAnchor.constraint(equalToConstant: 22).isActive = true
            backButton.heightAnchor.constraint(equalToConstant: 36).isActive = true
        }
        
        if viewModel.helpAction != nil {
            helpButton.addTarget(self, action: #selector(tapHelp), for: .touchUpInside)
            addSubview(helpButton)
            
            helpButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -30).isActive = true
            helpButton.topAnchor.constraint(equalTo: topAnchor, constant: 36).isActive = true
            helpButton.widthAnchor.constraint(equalToConstant: 22).isActive = true
            helpButton.heightAnchor.constraint(equalToConstant: 36).isActive = true
        }
    }

    @objc func tapBack() {
        viewModel.backAction?()
    }
    
    @objc func tapHelp() {
        viewModel.helpAction?()
    }
}

问题原因及修复方案

1. backButton初始化时错误绑定Target

问题:在backButton的闭包初始化代码中,将Target设置为HeaderView.self(类本身),但tapBack是实例方法,类无法响应实例方法的事件,直接导致按钮点击无响应。后续在loadView中重复添加的正确Target也会被初始的错误Target干扰。

修复:删除backButton初始化闭包中的错误Target绑定代码,只保留loadView中针对实例self的绑定:

var backButton: UIButton = {
    let button = UIButton()
    button.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
    // 移除错误的Target绑定行
    button.backgroundColor = UIColor.red
    button.tag=5
    button.translatesAutoresizingMaskIntoConstraints = false
    button.backgroundColor = .clear
    button.tintColor = .white
    button.setImage(UIImage(named: "back"), for: .normal)
    return button
}()

2. (潜在问题)值类型ViewModel的闭包引用问题

问题:HeaderViewModel是struct(值类型),传入HeaderView时会发生值拷贝,若后续闭包引用的上下文被意外覆盖,可能导致闭包无法正确执行。

修复(可选):将HeaderViewModel改为class(引用类型),避免值拷贝带来的问题:

class HeaderViewModel: HeaderViewModelProtocol {
    let title: String
    var color: UIColor = UIColor.white
    var imageName: String?
    var backAction: (() -> Void)?
    var helpAction: (() -> Void)?
    
    init(title: String) {
        self.title = title
    }
}

3. 验证HeaderView布局完整性

排查点:确保HeaderView被正确添加到ViewController的视图层级中,并且有明确的尺寸约束(比如设置heightAnchor),避免因父视图尺寸为0导致按钮超出交互区域。可以给HeaderView设置临时背景色,确认其布局范围是否符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 16:43:14