简体   繁体   中英

UIButton add target without sender

I have a UIDatePicker . After a date is selected I call this method

func goToNextScreen(selectedDate: Date) {
    //...
}

Now I have added a UIButton . In that button action I want to call the same method goToNextScreen without any date value. Date value is optional in next screen. I tried the following code

btn.addTarget(self, action: #selector(goToNextScreen), for: .touchUpInside)//goToNextScreen(_:)

@objc func goToNextScreen(selectedDate: Date? = nil) {
    //...
}

When the button is tapped the app crashes.

How to solve this without adding another method? If it is not possible why my approach doesn't work

What is happening here is, the button's internal logic is trying to pass the sender, which is a UIButton into your method's Date parameter. However, the sender parameter won't get passed if your method don't have any arguments.

Optional parameters don't really work in this situation. What you can do however, is to create another parameterless overload for goToNextScreen :

@objc func goToNextScreen() {
    goToNextScreen(selectedDate: nil)
}

And change

btn.addTarget(self, action: #selector(goToNextScreen), for: .touchUpInside)

to

btn.addTarget(self, action: #selector(goToNextScreen as () -> Void), for: .touchUpInside)

so that it different between the two overloads.

Note that the reason why just writing #selector(goToNextScreen) is ambiguous is because you have two methods named goToNextScreen , and Swift needs to resolve to one of them. But it can't with just the name. Here is a similar situation:

class Foo {
    @objc func f() {}
    func f(x: Int) {}
    let selector: Selector = #selector(f) // ambiguous use of f
}

Edit: You can't really do this without creating another method. Selectors are inflexible things.

It's not possible in this case re-use the same method. You should create a new one without parameters or whose parameters are UIButton (or a more generic type, often is Any ) and the UIEvent . Here's the explanation of the Target-Action mechanism: UIControl .

I thought you've to try this

var selectedDate = Date()
btn.addTarget(self, action: #selector(goToNextScreen), for: .touchUpInside)//goToNextScreen(_:)

@objc func goToNextScreen(_ sender: UIButton) 
{     

   selectedDate ?? self.datePickerDate : Date()    
}

Using Any as sender type and casting to Date works

//add button Target
btn.addTarget(self, action: #selector(goToNextScreen(_:)), for: .touchUpInside)

//Call with date value
goToNextScreen(Date())

@objc func goToNextScreen(_ selectedDate: Any) {
    nextVC.date = selectedDate as? Date
    //...
}

sender paramter of UIButton is casted to Date which is reason of crash , it should be a UIButton

btn.addTarget(self, action: #selector(btnClicked), for: .touchUpInside)//goToNextScreen(_:) 

@objc func btnClicked(_ sender:UIButton)  {
  // call next here
  goToNextScreen()
}
func goToNextScreen(_ selectedDate: Date? = nil) {
   if let date = selectedDate { }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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