简体   繁体   English

将选择器从UITableViewCell类添加到UITableViewController类

[英]Add a Selector from a UITableViewCell class to the UITableViewController class

I'm trying do design my own UITableViewCell to include a Stepper in the AccessoryView. 我正在尝试设计自己的UITableViewCell以在AccessoryView中包含一个步进器。 Therefore, I would like to add selector so that the ViewController would handle the behaviour of the value being changed. 因此,我想添加选择器,以便ViewController可以处理更改值的行为。 (The UITableViewCell should only be responsible to display the result in its label) (UITableViewCell应该只负责在其标签中显示结果)

Currently, I'm able to modify the label's text by using addTarget to the UITableViewCell class, but when I add a target to the UITableViewController class, I get the following error: 当前,我可以通过使用addTarget到UITableViewCell类来修改标签的文本,但是当我将目标添加到UITableViewController类时,出现以下错误:

2018-10-08 20:04:39.157912-0400 MyProject[1003:45273] -[MyProject expansionSelectionValueDidChange:]: unrecognized selector sent to instance 0x7fa88700fc00
2018-10-08 20:04:39.167219-0400 MyProject[1003:45273] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyProject.PlayerSelectionCell expansionSelectionValueDidChange:]: unrecognized selector sent to instance 0x7fa88700fc00'

I'm using X-Code 10 and Swift 4 to develop an iOS 12 application. 我正在使用X-Code 10和Swift 4开发iOS 12应用程序。

Here's the code to the UITableViewCell : 这是UITableViewCell的代码:

class PlayerSelectionCell: UITableViewCell {

    @objc func stepperValueDidChange(_ sender: UIStepper) {
        textLabel?.text = "\(newValue) Players"
    }

    override func awakeFromNib() {
        let stepper = UIStepper()
        stepper.addTarget(self, action: #selector(stepperValueDidChange(_:)), for: .valueChanged)
        stepper.addTarget(self, action: #selector(SetupViewController.expansionSelectionValueDidChange(_:)), for: .valueChanged)
        self.accessoryView = stepper
    }
}

Here's the code to my UITableViewController 这是我的UITableViewController的代码

class SetupViewController: UITableViewController {

    @IBOutlet var setupTableView: UITableView!

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        switch section {
        case 0:
            return "Expansions"
        case 1:
            return "Players"
        default:
            return nil
        }
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        switch section {
        case 0:
            return Expansions.allCases.count
        case 1:
            return 3
        default:
            return 0
        }
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.section == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Expansion Selection", for: indexPath) as? ExpansionCell ?? ExpansionCell()
            cell.textLabel?.text = Expansions.allCases[indexPath.row].rawValue
            return cell
        } else if indexPath.section == 1 && indexPath.row == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Player Selection", for: indexPath) as? PlayerSelectionCell ?? PlayerSelectionCell()
            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Player Detail", for: indexPath) as? PlayerCell ?? PlayerCell()
            cell.textLabel?.text = "Player \(indexPath.row)"
            return cell
        }
    }

    @objc func playerCountValueDidChange(_ sender: UIStepper) {
        print("Value : \(sender.value)")
    }

    @objc func expansionSelectionValueDidChange(_ sender: UISwitch) {
        print("Value : \(sender.isOn)")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

In alternative to the accepted solution: 替代公认的解决方案:

Depending on case you can try use delegate instead action/target 根据情况,您可以尝试使用委托代替操作/目标

//Declare a delegate for the cell
protocol PlayerSelectionCellDelegate : class {
    func playerSelectionCell(_ cell:PlayerSelectionCell, didChangeValue:Double)
    func playerSelectionCell(_ cell:PlayerSelectionCell, expansionSelectionValueDidChange:Bool)
}

class PlayerSelectionCell: UITableViewCell {

    weak var delegate : PlayerSelectionCellDelegate?        //Set a weak var to allocate the delegate

    @objc func stepperValueDidChange(_ sender: UIStepper) {
        delegate?.playerSelectionCell(self, didChangeValue: sender.value)
    }

    @objc func expansionSelectionValueDidChange(_ sender: UISwitch) {
        delegate?.playerSelectionCell(self, expansionSelectionValueDidChange: sender.isOn)
    }

    override func awakeFromNib() {
        let stepper = UIStepper()
        stepper.addTarget(self, action: #selector(stepperValueDidChange), for: .valueChanged)

        //This line should be reference to the UISwitch Action
        selectionSwitch.addTarget(self, action: #selector(expansionSelectionValueDidChange), for: .valueChanged)
        self.accessoryView = stepper
    }
}

In the ViewController you change the code to get in conformity to the delegate and set the ViewController as delegated to cell 在ViewController中,您可以更改代码以使其符合委托,并将ViewController设置为委托给单元格

class SetupViewController: UITableViewController {

    @IBOutlet var setupTableView: UITableView!

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }

    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        switch section {
        case 0:
            return "Expansions"
        case 1:
            return "Players"
        default:
            return nil
        }
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        switch section {
        case 0:
            return Expansions.allCases.count
        case 1:
            return 3
        default:
            return 0
        }
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.section == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Expansion Selection", for: indexPath) as? ExpansionCell ?? ExpansionCell()
            cell.textLabel?.text = Expansions.allCases[indexPath.row].rawValue
            return cell
        } else if indexPath.section == 1 && indexPath.row == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Player Selection", for: indexPath) as? PlayerSelectionCell ?? PlayerSelectionCell()
            cell.delegate = self            //Set the ViewController to be delegated by cell
            return cell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Player Detail", for: indexPath) as? PlayerCell ?? PlayerCell()
            cell.textLabel?.text = "Player \(indexPath.row)"
            return cell
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    //Remove the actions and implements the delegate methods
    func playerSelectionCell(_ cell: PlayerSelectionCell, didChangeValue players: Double) {
        print("Value Players: \(players)")
    }

    func playerSelectionCell(_ cell: PlayerSelectionCell, expansionSelectionValueDidChange selected: Bool) {
        print("Value Selected: \(selected)")
    }

}

Actually, the problem is that the addTarget(_ target, ...) method invokes the selector on target . 实际上,问题在于addTarget(_ target, ...)方法调用target上的选择器。 Even if you specify it like you did with full path. 即使您像使用完整路径一样指定它。

What you can try is to set target to nil and it should work then if the documentation is right: 您可以尝试将target设置为nil ,如果文档正确,它应该可以正常工作:

The target object—that is, the object whose action method is called. 目标对象,即,其操作方法被调用的对象。 If you specify nil, UIKit searches the responder chain for an object that responds to the specified action message and delivers the message to that object. 如果指定nil,则UIKit会在响应程序链中搜索对指定的操作消息作出响应的对象,并将该消息传递给该对象。

And if that doesn't help, specifying the target exactly should solve it. 如果这样做没有帮助,则确切地指定目标应该可以解决它。 You will just have to pass a reference to the controller. 您只需要将引用传递给控制器​​。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM