Swift “;无法识别的选择器发送到实例”;调用addTarget时在UIView类中

Swift “;无法识别的选择器发送到实例”;调用addTarget时在UIView类中,swift,class,uibutton,selector,swift5,Swift,Class,Uibutton,Selector,Swift5,我试图创建一个DropDownMenu类,但是当我试图调用其中一个按钮的addTarget时,出现了这个错误 发送到实例0x7fd167f06120'的无法识别的选择器以NSException类型的未捕获异常终止 我真的很感激任何帮助,没有答案是不好的 这是我的全班同学 class DropDownMenu: UIView { // Main button or Pre var main: UIButton! = UIButton(frame: CGRect(x: 0, y: 0

我试图创建一个DropDownMenu类,但是当我试图调用其中一个按钮的addTarget时,出现了这个错误

发送到实例0x7fd167f06120'的无法识别的选择器以NSException类型的未捕获异常终止

我真的很感激任何帮助,没有答案是不好的

这是我的全班同学

class DropDownMenu: UIView {
    // Main button or Pre
    var main: UIButton! = UIButton(frame: CGRect(x: 0, y: 0, width: 46, height: 30))
    var view: UIView!
    
    
    // Title
    var buttonTitles: [String]! = [""]
    var titleColor: UIColor! = UIColor.black
    var font: UIFont! = UIFont.systemFont(ofSize: 18)
    // Individual Button
    var buttonsBorderWidth: CGFloat! = 0
    var buttonsBorderColor: UIColor? = UIColor.white
    var buttonsCornerRadius: CGFloat! = 0
    var color: UIColor! = UIColor.clear
    // Button Images
    var buttonsImageEdgeInsets: UIEdgeInsets? = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
    var images: [UIImage]? = nil
    // Onclick stuff
    var target: UIViewController!
    
    private var currentSelected: String? = nil
    
    private var optionsStack = UIStackView()
    
    init(main: UIButton) {
        self.main = main
        super.init(frame: CGRect())
    }
    
    func createDropDownMenu() {
        main.addTarget(target, action: #selector(DropDownMenu.openDropdown(_:)), for: .touchUpInside)
        
        print("Button Target?: \(main.allTargets), self.target: \(String(describing: target))")
        
        let mainFrame = main.frame
        
        optionsStack.frame = CGRect(x: mainFrame.minX, y: mainFrame.maxY, width: mainFrame.width, height: CGFloat(buttonTitles.count) * mainFrame.height)
        optionsStack.axis = .vertical
        
        view.addSubview(optionsStack)
                
        var y: CGFloat! = 0
        
        for title in buttonTitles {
            let button = UIButton(frame: CGRect(x: 0, y: y, width: mainFrame.width, height: mainFrame.height))
            button.setTitle(title, for: .normal)
            button.setTitleColor(titleColor, for: .normal)
            button.backgroundColor = color
            button.titleLabel?.font = font
            button.addTarget(target, action: #selector(DropDownMenu.onclick), for: .touchUpInside)
            
            y += mainFrame.height
            
            optionsStack.addArrangedSubview(button)
        }
        
        for button in optionsStack.arrangedSubviews {
            button.isHidden = true
            button.alpha = 0
        }
    }
    
    @objc private func openDropdown(_ sender: UIButton) {
        print("sender: \(String(describing: sender))")
        optionsStack.arrangedSubviews.forEach { (button) in
            UIView.animate(withDuration: 0.7) {
                button.isHidden = !button.isHidden
                button.alpha = button.alpha == 0 ? 1 : 0
                self.view.layoutIfNeeded()
            }
        }
    }
    
    @objc private func onclick(_ sender: UIButton) {
        let title = sender.titleLabel!.text
        
        print(title as Any)
        
        main.setTitle(title, for: .normal)
        
        optionsStack.arrangedSubviews.forEach { (button) in
            UIView.animate(withDuration: 0.7) {
                button.isHidden = true
                button.alpha = 0
            }
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
以下是ViewController中对象的代码和创建

let grade = UIButton(frame: CGRect(x: 50, y: 300, width: 80, height: 30))
grade.layer.borderWidth = 1
grade.setTitle("Grade", for: .normal)
grade.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
grade.setTitleColor(UIColor.black, for: .normal)
        
let gradeDP = DropDownMenu(main: main)
gradeDP.buttonTitles = ["Title 1", "Title 2", "Title 3"]
gradeDP.color = UIColor.gray
gradeDP.target = self
gradeDP.titleColor = UIColor.white
gradeDP.view = view

view.addSubview(grade)
gradeDP.createDropDownMenu()
createDropDownMenu()函数中的第一个print语句打印

按钮目标?:[AnyHashable()],self.Target:Optional()

在mightknow的帮助下编辑之后,我想出了这个类。它没有任何针对main按钮的onclick操作

class DropDownMenu: UIStackView {
    
    var options: [String]! = [] // Labels for all of the options
    var titleButton: UIButton! = UIButton() // The Main Title Button
    
    init(options: [String]) {
        self.options = options
        let mainFrame = titleButton.frame
        
        super.init(frame: CGRect(x: mainFrame.minX, y: mainFrame.maxY, width: mainFrame.width, height: mainFrame.height * CGFloat(options.count)))
        
        var y: CGFloat = 0
        for title in self.options {
            let button = UIButton(frame: CGRect(x: 0, y: y, width: self.frame.width, height: mainFrame.height))
            button.setTitle(title, for: .normal)
            button.setTitleColor(titleButton.titleLabel?.textColor, for: .normal)
            button.backgroundColor = titleButton.backgroundColor
            button.addTarget(self, action: #selector(dropDownOptionClicked(_:)), for: .touchUpInside)
            
            button.isHidden = true
            button.alpha = 0
            
            self.addArrangedSubview(button)
            
            y += 1
        }
    }
    
    @objc func openDropDown(_ sender: UIButton) {
        print("Open DropDownMenu")
        for button in self.arrangedSubviews {
            UIView.animate(withDuration: 0.7) {
                button.isHidden = !button.isHidden
                button.alpha = button.alpha == 0 ? 1 : 0
                self.layoutIfNeeded()
                self.superview?.layoutIfNeeded()
            }
        }
    }
    
    @objc private func dropDownOptionClicked(_ sender: UIButton) {
        print(sender.titleLabel?.text as Any)
    }
    
    init() {
        super.init(frame: CGRect.zero)
    }
    
    required init(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}
而我的ViewController是

let dp = DropDownMenu(options: ["Label 1", "Label 2", "Label 3"])
        
let titleButton = UIButton(frame: CGRect(x: 100, y: 300, width: 180, height: 40))
titleButton.backgroundColor = UIColor.white
titleButton.setTitle("DropDownMenu", for: .normal)
titleButton.setTitleColor(UIColor.black, for: .normal)
titleButton.layer.borderWidth = 2
titleButton.addTarget(self, action: #selector(dp.openDropDown(_:)), for: .touchUpInside)
        
dp.titleButton = titleButton
错误

按钮目标?:[AnyHashable()],self.Target:Optional()


仍然出现,我不知道为什么。

您将目标设置为
UIViewController
,而您调用的方法实际上是
下拉菜单
类的方法。您需要做的是将目标设置为
self
,而不是
target
属性:

main.addTarget(self, action: #selector(DropDownMenu.openDropdown(_:)), for: .touchUpInside)
编辑:作为对您评论的回应,下面是我用来测试它的代码。我做了一些布局/颜色选择,只是为了让自己清楚,但这很有效:

class ViewController: UIViewController {
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view.
    let main = UIButton(frame: CGRect(x: 0, y: 100, width: 80, height: 30))
    main.layer.borderWidth = 1
    main.setTitle("Grade", for: .normal)
    main.titleLabel?.font = UIFont.boldSystemFont(ofSize: 20)
    main.setTitleColor(UIColor.black, for: .normal)
            
    let gradeDP = DropDownMenu(main: main)
    gradeDP.buttonTitles = ["Title 1", "Title 2", "Title 3"]
    gradeDP.color = UIColor.gray
    gradeDP.target = self
    gradeDP.titleColor = UIColor.white
    gradeDP.view = UIView()
    
    self.view.addSubview(gradeDP)
    let b = self.view.bounds
    gradeDP.frame = CGRect(x: b.minX, y: b.minY, width: b.width, height: b.height/2)
    gradeDP.backgroundColor = UIColor.purple
    gradeDP.target = self
    gradeDP.addSubview(gradeDP.main)
    gradeDP.createDropDownMenu()
}}
至于您的代码,我假设您在问题的第二部分中添加的代码在ViewController的
viewDidLoad()
方法中,并且用于初始化
下拉菜单的
main
变量是ViewController的一个实例变量,因为我没有在范围内的其他任何地方看到它。如果是这样的话,肯定有一些问题。它们是:

  • 实际上,您从未将
    gradeDP
    添加到视图层次结构中。如果这是行
    gradeDP.view=view
    应该做的,那就不是了。该代码实际执行的操作是将
    gradeDP
    view
    属性设置为对ViewController的
    view
    属性的引用。而且,除非您的DropDownMenu类中有未包含的代码,否则您实际上并没有将该引用用于任何事情。因此,您可以完全删除该行以及
    下拉菜单
    类中的
    视图
    属性。如果您试图将ViewController的视图设置为
    gradeDP
    ,则该代码将为
    self.view=gradeDP
    ,但我不建议这样做。
    UIViewController
    view
    属性用于某些特殊功能,可能不应该太麻烦。您可能希望添加
    gradeDP
    作为子视图,就像我在上面的代码中所做的那样

  • 您创建的
    grade
    按钮不被下拉菜单使用。我猜您是想用它来初始化,而不是
    main
    变量(这超出了代码的范围),如下所示:

    class DropDownMenu : UIView {
    
    var dropdownOptions : [String] = []
    private var titleButton : UIButton = UIButton()
    private var optionsStack : UIStackView = UIStackView()
    private var optionsButtons : [UIButton] = []
    
    @objc private func openDropdown(_ sender: UIButton) {
        // Add code to make dropdown options appear. There are multiple ways of doing this. For instance, the optionsButtons could be hidden and then unhidden when it's clicked, or they could be created only once the button is clicked.
    }
    
    @objc private func selectedOption(_ sender: UIButton) {
        // Code here for when option is selected
    }
    
    init(options: [String]) {
        self.dropdownOptions = options
        super.init(frame: CGRect.zero)
        // Customize all of your subviews here, and add them to your DropDownMenu (as subviews)
        // Add openDropdown(_:) target to self.titleButton
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    }
    
    let gradeDP=下拉菜单(主:年级)


  • 简而言之,除非您在其他地方没有共享代码,否则您上面的代码所做的就是创建一个标记为“Grade”的UIButton,它是可见的,但实际上没有做任何事情(并且不是您的下拉菜单的一部分),以及一个实际上不可见的下拉菜单,但会有一个调用
    openDropdown(:)的
    main
    按钮
    如果是。我猜这不是它的工作原理。不过,希望我上面提供的代码能帮助您达到您想要的目的。

    至于重建类以使其正常工作的建议,您可能需要从以下内容开始:

    class DropDownMenu : UIView {
    
    var dropdownOptions : [String] = []
    private var titleButton : UIButton = UIButton()
    private var optionsStack : UIStackView = UIStackView()
    private var optionsButtons : [UIButton] = []
    
    @objc private func openDropdown(_ sender: UIButton) {
        // Add code to make dropdown options appear. There are multiple ways of doing this. For instance, the optionsButtons could be hidden and then unhidden when it's clicked, or they could be created only once the button is clicked.
    }
    
    @objc private func selectedOption(_ sender: UIButton) {
        // Code here for when option is selected
    }
    
    init(options: [String]) {
        self.dropdownOptions = options
        super.init(frame: CGRect.zero)
        // Customize all of your subviews here, and add them to your DropDownMenu (as subviews)
        // Add openDropdown(_:) target to self.titleButton
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    }
    
    为类的原始版本编写的许多代码都可以放在函数中。另外,原始版本中有很多不必要的代码。例如,一旦修复了原始错误问题,
    target
    变量就不再使用,
    view
    变量已过时,
    createDropDownMenu()
    函数是不必要的,因为所有这些代码都可以放在init(选项:)或openDropdown(:)函数中

    然后,如果您选择使用该模板构建类,您将在ViewController的
    viewDidLoad()
    方法中使用以下代码实现它:

    let dropdown = DropDownMenu(titles: ["Title 1", "Title 2", "Title 3"])
    self.view.addSubview(dropdown)
    // Follow that with layout code that ensures it's the proper size and in the proper location
    
    我希望结合我的评论有意义,有帮助,并且不会太过势不可挡。我建议您启动一个新的空项目(或目标),构建类并将其添加到ViewController中,而不包含任何其他内容。这是一个很好的方法来隔离它,检查并确保一切看起来都正常。如果您需要关于如何构建类的其他建议,您可以尝试将DropDownMenu设置为
    UIStackView
    (而不是
    UIView
    )的子类,主按钮和所有选项按钮都排列在子视图中。这实际上可能更简单,因为如果您愿意的话,它有点省去了中间人,打开/关闭下拉列表时需要做的就是从
    .arrangedSubviews
    属性中添加/删除视图

    同样重要的是,如果视图需要将信息(例如选择了哪个选项)传递回ViewController,请确保对ViewController的引用标记为
    ,这样就不会创建保留循环


    最后一点,如果您对没有快速修复原始类的方法感到失望,并希望继续尝试,那么可能会有一些方法拼凑出一个解决方案(如我第一个答案中的代码,它确实有效…),但最终可能只会导致更多问题。所以,祝你一切顺利。

    我终于明白了!目标必须是下拉菜单

    titleButton.addTarget(dp, action: #selector(dp.openDropDown(_:)), for: .touchUpInside)
    
    下面是代码的其余部分

    let titleButton = UIButton(frame: CGRect(x: 50, y: 290, width: 100, height: 40))
    titleButton.backgroundColor = UIColor.white
    titleButton.setTitle("Grade", for: .normal)
    titleButton.setTitleColor(UIColor.black, for: .normal)
    titleButton.layer.borderWidth = 2
    titleButton.layer.cornerRadius = 10
            
    let dp = DropDownMenu(options: ["1", "Freshman", "Sophomore", "Junior", "Senior", "College"])
    dp.titleButton = titleButton
    dp.target = self
    dp.borderWidth = 2
    dp.spacing = 5
    dp.cornerRadius = 10
    dp.bgColor = UIColor.white
    
    将其添加到子视图并创建它。。