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

Xcode Swift项目中单选按钮点击Selector函数未触发问题

UIButton Selectors Not Triggering in Custom Swift UIView Subclass

Hey there, let's dig into why your radio button taps aren't firing the selector functions in your SortView subclass. I've gone through your code and spotted a few key issues that could be causing this, plus straightforward fixes to get things working.

Your Code Context

First, here's the relevant code you shared for reference:

SortView Implementation

class SortView: UIView {
    // MARK: - Properties
    lazy var driverSortRadioButton: UIButton = {
        let button = UIButton(type: .system)
        button.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
        button.setDimensions(height: 25, width: 25)
        button.backgroundColor = .clear
        button.contentMode = .scaleAspectFill
        button.addTarget(self, action: #selector(handleDriverSortRadioButton), for: .touchUpInside)
        return button
    }()
    
    lazy var pickupTimeSortRadioButton: UIButton = {
        let button = UIButton(type: .system)
        button.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
        button.setDimensions(height: 25, width: 25)
        button.backgroundColor = .clear
        button.contentMode = .scaleAspectFill
        button.addTarget(self, action: #selector(handlePickupTimeSortRadioButton), for: .touchUpInside)
        return button
    }()
    
    private let driverSortTitleLabel: UILabel = {
        let label = UILabel()
        label.text = "Sort by driver:"
        label.textAlignment = .left
        label.textColor = .white
        label.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
        label.backgroundColor = .clear
        return label
    }()
    
    private let pickupTimeSortTitleLabel: UILabel = {
        let label = UILabel()
        label.text = "Sort by pickup time:"
        label.textAlignment = .left
        label.textColor = .white
        label.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
        label.backgroundColor = .clear
        return label
    }()
    
    private let driverSortTextField: UITextField = {
        let tf = UITextField()
        tf.textColor = .black
        tf.textAlignment = .center
        tf.placeholder = "Louise"
        tf.font = UIFont(name: "AvenirNext-DemiBold", size: 15)
        tf.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
        tf.setWidth(width: 150)
        tf.layer.cornerRadius = 5
        return tf
    }()
    
    private let pickupTimeSortTextField: UITextField = {
        let tf = UITextField()
        tf.textColor = .black
        tf.textAlignment = .center
        tf.placeholder = "2:00pm"
        tf.font = UIFont(name: "AvenirNext-DemiBold", size: 15)
        tf.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
        tf.setWidth(width: 150)
        tf.layer.cornerRadius = 5
        return tf
    }()
    
    private enum radioButtonStates {
        case driver
        case pickupTime
    }
    
    private var radioButtonState = radioButtonStates.driver
    
    // MARK: - Lifecycle
    override init(frame: CGRect) {
        super.init(frame: frame)
        configureUI()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Selectors
    @objc func handleDriverSortRadioButton() {
        print("DEBUG: driver sort radio button clicked")
        if radioButtonState != .driver {
            pickupTimeSortRadioButton.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
            pickupTimeSortTextField.isEnabled = false
            pickupTimeSortTextField.text = ""
            radioButtonState = .driver
            driverSortRadioButton.setImage(UIImage(named: "Radio Button - Selected"), for: .normal)
            driverSortTextField.isEnabled = true
            driverSortTextField.text = ""
        }
    }
    
    @objc func handlePickupTimeSortRadioButton() {
        print("DEBUG: pickup time sort radio button clicked")
        if radioButtonState != .pickupTime {
            driverSortRadioButton.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
            driverSortTextField.isEnabled = false
            driverSortTextField.text = ""
            radioButtonState = .pickupTime
            pickupTimeSortRadioButton.setImage(UIImage(named: "Radio Button - Selected"), for: .normal)
            pickupTimeSortTextField.isEnabled = true
            pickupTimeSortTextField.text = ""
        }
    }
    
    // MARK: - Helper Functions
    private func configureUI() {
        let driverSortStackView = UIStackView(arrangedSubviews: [driverSortRadioButton, driverSortTitleLabel, driverSortTextField])
        driverSortStackView.axis = .horizontal
        driverSortStackView.distribution = .fill
        driverSortStackView.spacing = 5
        
        let pickupTimeSortStackView = UIStackView(arrangedSubviews: [pickupTimeSortRadioButton, pickupTimeSortTitleLabel, pickupTimeSortTextField])
        pickupTimeSortStackView.axis = .horizontal
        pickupTimeSortStackView.distribution = .fill
        pickupTimeSortStackView.spacing = 5
        
        let stackView = UIStackView(arrangedSubviews:[driverSortStackView, pickupTimeSortStackView])
        stackView.axis = .vertical
        stackView.distribution = .fill
        stackView.spacing = 10
        
        self.addSubview(stackView)
        stackView.centerX(inView: self)
        stackView.centerY(inView: self)
    }
}

Parent ViewController Setup

private lazy var sortView = SortView()

// In viewDidLoad or similar
view.addSubview(sortView)
sortView.anchor(top:tableView.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 40, paddingLeft: 32, paddingRight: 32)

Root Cause Analysis

The most likely culprit here is incorrect layout constraints leading to zero or invalid frame size for your SortView and its buttons. Here's why:

  1. You're only setting top, left, and right constraints for sortView in the parent VC—you never define a height constraint. This means sortView has an implicit height of 0, even if its internal stack view appears to render on screen.
  2. Inside SortView, you're centering the stack view using centerX(inView: self) and centerY(inView: self)—but if the parent (SortView) has no height, the stack view's position and size become invalid, making the buttons unclickable (their actual tap area is outside the visible screen or non-existent).

Other minor checks to rule out:

  • Ensure isUserInteractionEnabled is true for SortView, the stack views, and the buttons (default is true, but it's worth confirming).
  • Verify your button images exist in the asset catalog—missing images can sometimes cause unexpected layout issues.

Fixes to Get Taps Working

Let's implement these step-by-step fixes:

1. Add a Height Constraint to SortView

Update your parent VC's layout code to give sortView a clear height, or let it automatically size based on its content:

Option A: Fixed Height Constraint
view.addSubview(sortView)
sortView.anchor(top: tableView.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, paddingTop: 40, paddingLeft: 32, paddingRight: 32)
// Add a fixed height (adjust based on your content)
sortView.heightAnchor.constraint(equalToConstant: 100).isActive = true
Option B: Auto-Size to Content (Better Approach)

To make SortView automatically adjust its height to fit the internal stack view, first modify SortView to expose the stack view as a property:

// Inside SortView, replace the local stackView with a private property
private let stackView: UIStackView = {
    let stack = UIStackView()
    stack.axis = .vertical
    stack.distribution = .fill
    stack.spacing = 10
    return stack
}()

// Then update configureUI()
private func configureUI() {
    let driverSortStackView = UIStackView(arrangedSubviews: [driverSortRadioButton, driverSortTitleLabel, driverSortTextField])
    driverSortStackView.axis = .horizontal
    driverSortStackView.distribution = .fill
    driverSortStackView.spacing = 5
    
    let pickupTimeSortStackView = UIStackView(arrangedSubviews: [pickupTimeSortRadioButton, pickupTimeSortTitleLabel, pickupTimeSortTextField])
    pickupTimeSortStackView.axis = .horizontal
    pickupTimeSortStackView.distribution = .fill
    pickupTimeSortStackView.spacing = 5
    
    stackView.addArrangedSubview(driverSortStackView)
    stackView.addArrangedSubview(pickupTimeSortStackView)
    
    self.addSubview(stackView)
    // Pin the stack view to the edges of SortView instead of centering
    stackView.fillSuperview(padding: UIEdgeInsets(top: 10, left: 0, bottom: 10, right: 0))
}

Then in the parent VC, constrain sortView's height to match the stack view:

view.addSubview(sortView)
sortView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
    sortView.topAnchor.constraint(equalTo: tableView.bottomAnchor, constant: 40),
    sortView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32),
    sortView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32),
    sortView.heightAnchor.constraint(equalTo: sortView.stackView.heightAnchor, constant: 20) // Add padding if needed
])

2. Verify User Interaction is Enabled

Add these lines to SortView's init method to explicitly enable interaction (just to rule out any accidental overrides):

override init(frame: CGRect) {
    super.init(frame: frame)
    self.isUserInteractionEnabled = true
    driverSortRadioButton.isUserInteractionEnabled = true
    pickupTimeSortRadioButton.isUserInteractionEnabled = true
    configureUI()
}

3. Test with Visual Debugging

Temporarily add background colors to SortView and the buttons to see their actual frames:

// In SortView's init
self.backgroundColor = .blue // Remove after testing

// In button setup
button.backgroundColor = .red // Remove after testing

This will let you confirm if the buttons are actually within the bounds of SortView and have a valid tap area.


Final Notes

Once you implement these fixes, your button taps should start firing the selector functions correctly. The core issue was the missing height constraint on SortView—without it, the view (and its subviews) don't have a valid frame to receive touch events.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 15:52:54