Adding an observer to a UISwitch within a custom tableview cell - ios

This is a follow on from a previous question I have asked but I feel I am missing something very simple and its driving me up the wall!
I have a custom tableview cell which contains a switch and I need to trigger a function each time it's value is changed. I've tried using .addTarget but it never seems to trigger the function so maybe my selector syntax is incorrect.
I create the switch programatically within the tableview cell like this:
let thisSwitch: UISwitch = {
let thisSwitch = UISwitch()
thisSwitch.isOn = false
thisSwitch.translatesAutoresizingMaskIntoConstraints = false
thisSwitch.addTarget(self, action: Selector("switchTriggered:"), for: .valueChanged)
return thisSwitch
}()
Then directly below that I have my function:
func switchTriggered(sender: AnyObject) {
print("SWITCH TRIGGERED")
let sentSwitch = sender as! UISwitch
privateExercise.switchState = sentSwitch.isOn
}
It shows an error message stating " No method declared with Objective-C selector 'switchTriggered:' ". What am I missing here? Any help would be much appreciated!

The selector syntax should be
thisSwitch.addTarget(self, action: #selector(switchTriggered), for: .valueChanged)
Also keep the parameter as UISwitch type itself in order to avoid casting in function
func switchTriggered(sentSwitch: UISwitch) {
print("SWITCH TRIGGERED")
privateExercise.switchState = sentSwitch.isOn
}

Related

"addTarget" action function of UIButton not called (strikethrough)? [duplicate]

This question already has an answer here:
Why does Xcode line-out autocomplete methods for selector?
(1 answer)
Closed 2 years ago.
I have a UIButton within a UITableViewCell. When I call addTarget on the button and attempt to put a function in, the autocomplete has the function crossed out with a white line (see first image). Xcode still allows me to put the function in and run the app; however, when the button is tapped the function isn't called.
Button initialization:
private let infoButton: UIButton = {
let button = UIButton()
button.backgroundColor = .clear
button.adjustsImageWhenHighlighted = false
button.setImage(Images.sizeGuide, for: .normal)
button.addTarget(self, action: #selector(infoButtonPressed(_:)), for: .touchUpInside)
return button
}()
Function (infoButtonPressed):
#objc private func infoButtonPressed(_ sender: UIButton) {
print("Button Pressed")
}
Because I reuse this cell multiple times and only one of these cells needs to have this button, I have a variable that dictates whether or not to show the button:
var hasInfoButton: Bool = false {
didSet {
if hasInfoButton {
setupInfoButton()
}
}
}
The function that is called above simply sets up the button using autoLayout. Something to mention: when I tried calling addTarget in this function, the app crashed with Unrecognized selector sent to instance...
The tableView in which this is embedded in is only static and displays data. Therefore, allowSelection and allowsMultipleSelection are both set to false.
Does anyone have a solution to this?
You shouldn't need (_ sender: UIButton) the method should just be:
#objc func infoButtonPressed() {
print("button pressed")
}
EDIT:
The button initializer is also a little strange. The way I generally go about this kind of thing is like this:
let button = UIButton()
private func configureButton() {
button.backgroundColor = .clear
button.adjustsImageWhenHighlighted = false
button.setImage(Images.sizeGuide, for: .normal)
button.addTarget(self, action: #selector(infoButtonPressed(_:)), for: .touchUpInside)
}
Then call configureButton() in viewDidLoad()

how can I get tableview label value on button click in swift

I am new in swift and I want to get the value of label from tableview on button click
I am using code like this but it is getting crash
in cellforrowatindexpath
cell.btnsubmit.tag = indexPath.row
cell.btnsubmit.addTarget(self, action: #selector(buttonSelected), for: .touchUpInside)
#objc func buttonSelected(sender: UIButton){
print(sender.tag)
let cell = sender.superview?.superview as! PatientUpdateVCCell
surgery_date = cell.surgeryDateTextField.text!
discharge_date = cell.dischargeDateTextField.text!
follow_up_duration = cell.lblfolowup.text!
follow_up_date = cell.firstFollowUpTextField.text!
patient_status = cell.patientStatusTextView.text!
}
but it is getting crash. How can I achieve this
crash
Could not cast value of type 'UITableViewCellContentView' (0x11a794af0) to 'appname.PatientUpdateVCCell' (0x10ae74ae0).
According to your crash last superView is contentView then it's superView is the needed cell , so You need
let cell = sender.superview!.superview!.superview as! PatientUpdateVCCell
Target/action is pretty objective-c-ish. And view hierarchy math is pretty cumbersome.
A swiftier way is a callback closure which is called in the cell and passes the cell.
In the cell add a callback property and an IBAction. Connect the action to the button
var callback : ((UITableViewCell) -> Void)?
#IBAction func buttonSelected(_ sender: UIButton) {
callback?(self)
}
In cellForRow rather than the tag assign the closure
cell.callback = { currentCell in
self.surgery_date = currentCell.surgeryDateTextField.text!
self.discharge_date = currentCell.dischargeDateTextField.text!
self.follow_up_duration = currentCell.lblfolowup.text!
self.follow_up_date = currentCell.firstFollowUpTextField.text!
self.patient_status = currentCell.patientStatusTextView.text!
}
And delete the action method in the controller

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 { }
}

Having trouble targeting a DesignableView in a UITableViewCell upon tapping it in order to change

and thanks in advance for taking the time to help.
Inside my CellForRowAtIndexPath, I have the following line:
cell.timeView.addTarget(self, action: "ButtonDidPress:", forControlEvents: UIControlEvents.TouchUpInside)
and my selector function is:
func ButtonDidPress (sender: DesignableView!){
let view:DesignableView = sender
cell.timeView.shadowColor = UIColor.yellowColor()
table.reloadData()
}
and the error i get is:
unrecognized selector sent to instance
I'm thinking that perhaps one can't send a View as a selector (am I using the correct terminology?), but how else can I target that particular view in that cell?
UPDATE:
I also tried using gestureRecognizer instead:
var tap = UIGestureRecognizer(target: self, action: Selector( "viewDidTap:"))
cell.timeView.addGestureRecognizer(tap)
and
func viewDidTap (sender: DesignableView!){
but I got the same error here.
Thanks!
There's a couple of strange things happening in your code. It seems you want to change the shadowColor property of timeView when a user touch it, right?
Two possible solutions are:
(This one is IMO the better one) Change DesignableView to inherit from UIButton. Then you can set:
timeView.addTarget(self, action: "ButtonDidPress:", forControlEvents: .TouchUpInside). Make sure you set it just once for each cell. Otherwise you will get multiple calls on one tap.
Use UITapGestureRecognizer, but you should put it in your UITableViewCell subclass, not to the view controller. Also, the sender in viewDidTap is not the view itself, but the recognizer. So the method will go like this:
func viewDidTap(sender: UITapGestureRecognizer) {
let location = sender.locationInView(sender.view)
if timeView.hitTest(location, withEvent: nil) == timeView {
timeView.shadowColor = UIColor.yellowColor()
// table.reloadData() - you don't need to reload the table
}
}

How to call selector from switch only when change to state ON in Swift?

I need call a function with selector when change switch, but only when I set switch to On state:
cell.switchMy.addTarget(self, action: "openView", forControlEvents: UIControlEvents.ValueChanged)
func openView(){
var cell:CustomCell = self.tableView.dequeueReusableCellWithIdentifier("cell5") as CustomCell
if(cell.switchMy.on == true){
println("ON")
self.performSegueWithIdentifier("segueUnl", sender: self)
}
}
If I use this code, always call the function, but I 'm controlling if the switch state is ON... How can this correctly?
This is (nearly) the right way to do this. You can't have a method be called only when the switch state is on.
But you don't need to retrieve the cell from the openView method. The sender will be passed by the caller:
cell.switchMy.addTarget(self, action: "openView:", forControlEvents: UIControlEvents.ValueChanged)
func openView(sender: UISwitch){
if(sender.on) {
println("ON")
self.performSegueWithIdentifier("segueUnl", sender: self)
}
}
Be careful: the selector used in addTarget needs to be changed from openView to openView:

Resources