I'm working with UISlider and I want recognize a change of value:
var sliderGeofence: UISlider!
Inside my viewDidLoad():
self.sliderGeofence = UISlider(frame: CGRect(x: 20, y: 20, width: self.view.frame.size.width - 50, height: 50))
self.sliderGeofence?.maximumValue = 100000
self.sliderGeofence?.minimumValue = 500
self.sliderGeofence?.value = 100
self.sliderGeofence?.isUserInteractionEnabled=true
self.sliderGeofence?.addTarget(self, action: Selector("geofenceValueChange:"),for: UIControlEvents.valueChanged)
gmsMap.addSubview(self.sliderGeofence!)
sliderGeofence?.isHidden = true
My value-change method:
#IBAction func geofenceValueChange(sender: AnyObject)
{
print("entro")
}
The app loads the slider, however, when I change its value this happens:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MocaAdmin.FirstViewController
geofenceValueChange:]: unrecognized selector sent to instance
0x7fca61511480'
What am I doing wrong?
Change the target to the following:
sliderDemo.addTarget(self, action: #selector(geofenceValueChange(_:)), for: .valueChanged)
And then the function to:
func geofenceValueChange(_ sender:UISlider) {
print("entro")
}
No need for the #IBAction in the function.
You should be using the selector like this:
self.sliderGeofence?.addTarget(self, action: #selector(FirstViewController.geofenceValueChange(_:)), for: .valueChanged)
That way, Xcode will even suggest functions to use and autocomplete your typing to ensure things will work.
Hope that helps!
Related
I am fairly new to Swift, I am writing a plugin for a hybrid App (Cordova).
I managed to have the App rootController display a ViewController with a game inside and a separate UIView header at the top that contains a UILabel as a back button.
I am experiencing a crash as soon as I tap on the back button.
What am I doing wrong?
import SomeFramework;
#objc (MyIntegration) class MyIntegration : SomeFrameworkDelegate {
func implementedFunctionForDelegate(_ controller: FwViewController) {
print("fwViewControllerDidHandleSomeEvent");
}
// ...
#objc(launchGame:)
func launchGame() {
// Prep params
let params = FwLaunchParameters.init()
params.gameId = gameId
// ViewController init
let gameController = FwViewController.init(gameLaunchParameters: params)
gameController.delegate = self
// Display game using rootViewController
let currentWindow: UIWindow? = UIApplication.shared.keyWindow
currentWindow?.rootViewController?.present(gameController, animated: true, completion: nil)
let backHomeHeader = UIView(frame: CGRect(x: 0, y: 0, width: 400, height: 80.0))
let label = UILabel(frame: CGRect(x: 0, y: 28, width: 200, height: 50))
label.text = "\u{3008} Back Home"
label.isUserInteractionEnabled = true
let gestureRecognizer = UITapGestureRecognizer(target: currentWindow?.rootViewController, action: Selector("handleTap"))
label.addGestureRecognizer(gestureRecognizer)
handleTap() // prints out the log as expected
// Display header
currentWindow?.addSubview(backHomeHeader)
backHomeHeader.addSubview(label)
}
#objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {
print("tapped! yay!")
}
}
Error log right before the crash:
2021-07-31 01:17:24.790750-0400 MyApp[53004:2197131] -[MainViewController handleTap]: unrecognized selector sent to instance 0x7fdb44906490
2021-07-31 01:17:24.842133-0400 MyApp[53004:2197131] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MainViewController handleTap]: unrecognized selector sent to instance 0x7fdb44906490'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff20422fba __exceptionPreprocess + 242
1 libobjc.A.dylib 0x00007fff20193ff5 objc_exception_throw + 48
Note: I use UIApplication.shared.keyWindow because it's a hybrid App (Cordova) and I'm writing a plugin that allows some Javascript code to launch an html5 game using Apple On-Demand Resources.
Everything works fine except for the Back Home button...
Target is an object to which action should be sent. You need to move handleTap to your FwViewController or to replace target with self, like this:
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
Also always prefer #selector to Selector because it'll give you an error if you're using a wrong selector.
The crash says that the MainViewController has no method with the name handleTap
It seems currentWindow?.rootViewController is MainViewController and the handleTap function is in MyIntegration class.
So pass in self as the target or implement the handleTap inside the MainViewController for this case.
please bear with me, as I'm new to swift -4 weeks old-.
I've created the following 2 functions in fileA.swift
func custombttn(theSelector:Selector)-> UIButton{
let bttn = UIButton(frame: CGRect(x:20, y:400, width:200, height:30))
bttn.setTitle("tap this button", for: UIControlState.normal)
bttn.backgroundColor = UIColor.black
bttn.setTitleColor(UIColor.magenta, for: UIControlState.normal)
bttn.addTarget(bttn, action: theSelector, for: UIControlEvents.touchUpInside)
return bttn
}
func customtxtfld() -> UITextField{
let txtField = UITextField(frame: CGRect(x:20, y:360, width:200, height:30))
txtField.borderStyle = UITextBorderStyle.roundedRect
txtField.backgroundColor = UIColor.magenta
txtField.placeholder = "Do you like me now..?"
return txtField
}
The reason behind the custombttn(theSelector:Selector), is that i want to pass the function dynamically to the button in my viewcontroller file.
Now, moving the fileB.swift, I have the following code...
class TabOneViewController: UIViewController{
let txt = customtxtfld()
let bttn = custombttn(theSelector: #selector(updatetxt))
override func loadView() {
super.loadView()
view.addSubview(txt)
view.addSubview(bttn)
}
func updatetxt(){
txt.text = "hello, you!"
}
}
Here is where things get tricky, when I attempt to build, I don't get any error (not even a warning). However, when I run the app, and tap the bttn in fileB.swift, I get the following error during runtime:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[UIButton updatetxt]:
unrecognized selector sent to instance 0x7f8453415670'
If I have 2 or more functions in my fileB.swift that I wish to assign dynamically to the action part of the addTarget, is there any way I can pass the selector dynamically to a button..?
Appreciate your time and assistance. Please let me know if I need to explain something further.
It's crashing because your button target is wrong.
func custombttn(target:Any, theSelector:Selector)-> UIButton{
let bttn = UIButton(frame: CGRect(x:20, y:400, width:200, height:30))
bttn.setTitle("tap this button", for: UIControlState.normal)
bttn.backgroundColor = UIColor.black
bttn.setTitleColor(UIColor.magenta, for: UIControlState.normal)
bttn.addTarget(target, action: theSelector, for: UIControlEvents.touchUpInside)
return bttn
}
And use it like this
class TabOneViewController: UIViewController{
let txt = customtxtfld()
override func loadView() {
super.loadView()
view.addSubview(txt)
let bttn = custombttn(target:self, theSelector: #selector(updatetxt))
view.addSubview(bttn)
}
func updatetxt(){
txt.text = "hello, you!"
}
}
Yes, you can. The issue here is that you passed the button itself as the target for the action. Just pass the correct target when adding the action, which in this case is the instance of your view controller.
I've had this issue before but it usually due to not having a button hooked up in Storyboard or not passing a parameter when the function is expecting one, like most of the existing questions on here seem to suggest. This time however it is neither of those things.
I am creating a button in my TableView Cell by using this code in the CellForRowAt method:
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.addTarget(self, action: Selector(("showPeople:")), for: UIControlEvents.touchUpInside)
button.tag = indexPath.row
cell.addSubview(button)
and I have declared the showPeople method like so:
func showPeople(sender: UIButton) {
print("pressed")
}
When the button is pressed the program crashed with the following message:
showPeople: unrecognized selector sent to instance
But when I declare the method like so (remove the parameter):
func showPeople() {
print("pressed")
}
and change the selector to Selector(("showPeople")) it works fine which I guess means there is an issue with the way I'm passing the parameter. Why would this be happening? The function is expecting a parameter so the : is needed.
Looks like you're missing sender part in your selector.
Try this instead:
button.addTarget(self, action: #selector(ViewController.showPeople(sender:)), for: .touchUpInside)
I've added a button to a specific UITableViewCell. When I select the button, I get a crash:
ButtonTapped
libc++abi.dylib: terminating with uncaught exception of type NSException
At the beginning of cellForRowAt, I'm defining the button:
let myButton = UIButton(type: .custom)
myButton.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
myButton.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
myButton.tintColor = UIColor.yellow()
For the indexpath.row I attach the button like this:
cell.accessoryView = myButton as UIView
And the action buttonTapped tries to load a different ViewController.
I get confirmation that the button action worked (the routine was called).
The routine is as follows:
func buttonTapped() {
print("ButtonTapped")
let myPickerController = self.storyboard?.instantiateViewController(withIdentifier: "picker") as? MyPickerController
print("1")
self.present(myPickerController!, animated: true)
print("2")
}
As you can see from the log, I do see that the routine was called, but I do not see the print values 1 or 2 before the crash. Anyone see what I'm doing wrong?
Add target like,
myButton.addTarget(self, action: #selector(YourControllerName.buttonTapped(_:)), for: .touchUpInside)
then change your function like,
func buttonTapped(sender : UIButton){
....
}
Hope this helps you.
ok i'm trying to build a simple UISlider in swift and i always get the same error when i run my code, it's a sigbrt error and it gives me this error:
2015-06-03 22:36:52.659 myslider[2780:224039] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[myslider.ViewController sliderValueChanged:]: unrecognized selector sent to instance 0x7f92f1e25340'
* First throw call stack:
here's the code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var ValueLabel: UILabel!
var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
slider = UISlider(frame: CGRectMake(100, 100, 200, 23))
slider.minimumValue = 0
slider.maximumValue = 100
view.addSubview(slider)
slider.center = view.center
slider.value = slider.maximumValue / 3.0
slider.addTarget(self, action: "sliderValueChanged:", forControlEvents: UIControlEvents.ValueChanged)
slider.continuous = false
salueDidChange(slider)
ChangeColor()
thumbImage()
}
func salueDidChange(sender: UISlider){
ValueLabel.text = "\(sender.value)"
}
func ChangeColor(){
slider.maximumTrackTintColor = UIColor.redColor()
slider.minimumTrackTintColor = UIColor.greenColor()
}
func thumbImage(){
slider.setThumbImage(UIImage(named: "thumbNormal"), forState: UIControlState.Normal)
slider.setThumbImage(UIImage(named: "thumbHighlighted"), forState: UIControlState.Highlighted)
}
}
The error is telling you exactly what's wrong. You're creating a UISlider in code. You set up it's target like this:
slider.addTarget(
self,
action: "sliderValueChanged:",
forControlEvents: UIControlEvents.ValueChanged)
So when you change the value of your slider, the method is going to try to call a method "sliderValueChanged:" in your view controller. That method needs to take 1 parameter, a sender:
#IBAction func sliderValueChanged(sender: AnyObject)
{
//Do something with the new slider value.
}
The type of sender can also be type UISlider.
If you don't have a method with that signature in your view controller, you will crash when you change the slider value, just like you say you are.
EDIT:
As others have pointed out in their comments/answers, it looks like your target method is the misnamed method salueDidChange. You should rename it.
It's also a good idea to put the #IBAction tag on methods that will be called from controls. You must do that if you're going to define the control and connect it's action in Interface Builder (which is a good idea, rather than doing it in code.)
With slider.addTarget(self, action: "sliderValueChanged:", forControlEvents: UIControlEvents.ValueChanged) you're telling Swift to call the method sliderValueChanged: on your ViewController, but there is no such method in your ViewController. That's what the error is telling you. Most likely you wrote salueDidChange for the same purpose so you should rename salueDidChange to sliderValueChanged and add #IBAction.
If that doesn't work, try connecting the event handler from your storyboard:
Remove the slider.addTarget line of code.
Right click your slider in your storyboard. See if there's an event handler hooked up for the value changed event. If so remove it using the little x.
Ctrl-drag from your slider to your sliderValueChanged method to hook it up again.
Jonathan, I had a similar frustrating experience with unrecognized selector and UISlider. Others have already said salueDidChange is a typo but it isn't the only issue. The syntax for selector has been a source of confusion for newcomers (like me) as Swift evolved.
The correct syntax for selector is explained in a nut shell in this answer to an unrelated problem.
“Using #selector will check your code at compile time to make sure the
method you want to call actually exists. Even better, if the method
doesn’t exist, you’ll get a compile error: Xcode will refuse to build
your app, thus banishing to oblivion another possible source of bugs.”
Your question along with answers here helped me reach a working solution in Swift 3 below.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var ValueLabel: UILabel!
var slider: UISlider!
override func viewDidLoad() {
super.viewDidLoad()
slider = UISlider(frame: CGRect(x: 100, y: 100, width: 200, height: 23))
slider.minimumValue = 0
slider.maximumValue = 100
view.addSubview(slider)
slider.center = view.center
slider.value = slider.maximumValue / 3.0
slider.addTarget(self, action: #selector(sliderValueChanged), for: UIControlEvents.valueChanged)
slider.isContinuous = false
changeColor()
thumbImage()
}
func sliderValueChanged(sender: UISlider){
print(sender.value)
// ValueLabel.text = "\(sender.value)" // Note: not included in this test!!!
}
func changeColor(){
slider.maximumTrackTintColor = UIColor.red
slider.minimumTrackTintColor = UIColor.green
}
func thumbImage(){
slider.setThumbImage(UIImage(named: "thumbNormal"), for: UIControlState.normal)
slider.setThumbImage(UIImage(named: "thumbHighlighted"), for: UIControlState.highlighted)
}
}