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.
Related
I added a button on the main screen of the app and on the tap of a button, a new viewcontroller is presented.
This works completely fine in the simulator but as soon as I try in an actual iPhone, it causes the app to crash.
Also, the crash is only caused on the login button while the sign up button made the same way does work perfect
I will leave the code below
var loginButton = UIButton()
var signUpButton = UIButton()
loginButton.setTitle("Login", for: .normal)
loginButton.titleLabel?.textAlignment = .center
loginButton.backgroundColor = appGreenTheme
loginButton.titleLabel?.textColor = .white
loginButton.layer.cornerRadius = 20
loginButton.titleLabel?.font = UIFont.systemFont(ofSize: 20)
loginButton.setBackgroundImage(UIImage(named: "pinkOrangeGradientPDF"), for: .normal)
loginButton.clipsToBounds = true
signUpButton.setTitle("Sign Up", for: .normal)
signUpButton.setTitleColor(.black, for: .normal)
signUpButton.titleLabel?.textAlignment = .center
signUpButton.backgroundColor = .white
signUpButton.titleLabel?.textColor = .black
signUpButton.layer.cornerRadius = 20
signUpButton.titleLabel?.font = UIFont.systemFont(ofSize: 20)
loginButton.addTarget(self, action: #selector(loginButtonTapped1(_:)), for: .allTouchEvents)
signUpButton.addTarget(self, action: #selector(signUpButtonTapped1(_:)), for: .allTouchEvents)
///////////////////////////////////////////////////
#objc func loginButtonTapped1(_ sender: UIButton) {
let nav = UINavigationController(rootViewController: LoginViewController())
self.present(nav, animated: true, completion: nil)
}
#objc func signUpButtonTapped1(_ sender: UIButton) {
let nav = UINavigationController(rootViewController: SignUpViewController())
self.present(nav, animated: true, completion: nil)
}
I also tried with "touchUpInside" events. again it works perfectly in the simulator but not in a physical device.
Any help is welcome.
Below is the error shown in the logs
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SparkGPS.LoginView addTarget:action:forControlEvents:]: unrecognized selector sent to instance 0x13dd4c740'
The answer is in the error message. Somewhere, my guess is in LoginViewController, there is a view of type LoginView. That view is calling addTarget(_:action:for:). LoginView is not subclassed from UIControl and does not have addTarget(_:action:for:). It's causing the crash.
Let me break down the parts of -[SparkGPS.LoginView addTarget:action:forControlEvents:].
The - at the beginning means it's an instance method and not a static or class method.
SparkGPS.LoginView is the module and class. A module is another word for a framework or app. In this case, it looks like you have an app named SparkGPS and a class named LoginView.
addTarget:action:forControlEvents: is Objective-C's name for addTarget(_:action:for:).
Finally, "selector sent to instance" means the variable call a method. Selector is a way to identify a method, and an instance is stored in a variable. For example, in your code you have loginButton.setTitle("Login", for: .normal). This could be worded as setTitle(_:for:) was sent to the instance loginButton.
You can add a tap gesture recogniser to the button itself. It's best practice to use outlets, but this works fine and is useful for other UI components like views or labels too
let loginTapGesture = UITapGestureRecognizer(target: self,
action: #selector(loginButtonTapped1))
loginButton.addGestureRecognizer(loginTapGesture)
In a UIViewController (my BaseViewController below), I am adding a subview of a popup dialog view (from another class NotifDialog) containing a button.
This button should hide the popup dialog when touched.
My problem is that either the button doesn't detect touch or throws "unrecognized selector sent to instance"
class NotifDialog: NSObject {
var dialogView: UIView!
superView = controller.view
dialogView = UIView()
superView?.addSubview(dialogView)
dialogView.translatesAutoresizingMaskIntoConstraints = false
let dialogViewWidth = BaseViewController.screenWidth * 0.66
let dialogViewHeight = BaseViewController.screenHeight * 0.45
dialogView.centerXAnchor.constraint(equalTo: (superView?.safeAreaLayoutGuide.centerXAnchor)!).isActive = true
dialogView.centerYAnchor.constraint(equalTo: (superView?.safeAreaLayoutGuide.centerYAnchor)!).isActive = true
dialogView.widthAnchor.constraint(equalToConstant: dialogViewWidth).isActive = true
dialogView.heightAnchor.constraint(equalToConstant: dialogViewHeight).isActive = true
let bClose = UIButton()
dialogView?.addSubview(bClose)
bClose.translatesAutoresizingMaskIntoConstraints = false
let bCloseWidth = dialogViewWidth * 0.5
bClose.centerXAnchor.constraint(equalTo: dialogView.safeAreaLayoutGuide.centerXAnchor).isActive = true
bClose.topAnchor.constraint(equalTo: dialogView.topAnchor, constant: 25).isActive = true
bClose.widthAnchor.constraint(equalToConstant: bCloseWidth).isActive = true
bClose.heightAnchor.constraint(equalToConstant: 25).isActive = true
//bClose.addTarget(controller, action:#selector(self.hide(_:)), for: .touchUpInside) -> touch not detected
//bClose.addTarget(self, action:#selector(self.closeDialog(_:)), for: .touchUpInside) -> throws error "unrecognized selector sent to instance"
}
func closeDialog(_ sender : UIButton) {
dialogView.isHidden = true
}
.
class BaseViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let topViewController :UIViewController = self.navigationController!.topViewController!
let dialog = NotifDialog()
dialog.loadCustomViewIntoController(controller: topViewController)
}
Setting my button the following way makes it like touch is not detected
bClose.addTarget(self, action:#selector(closeDialog(_:)), for: .touchUpInside)
Setting my button the following way detects touches, but throws error : "unrecognized selector sent to instance"
bClose.addTarget(controller, action:#selector(closeDialog(_:)), for: .touchUpInside)
Finally, I also tried to make my function closeDialog static. In this case it doesn't crash anymore, however I can't use dialogView in it since this var is not static.
How can I hide my dialog from this button's touch ?
The closeDialog message is sent to the target you specified in the addTarget call. Since the closeDialog method exists on the NotifDialog class, you need to make the NotifDialog instance the target.
bClose.addTarget(
dialog, // This is the instance that will receive the message
action:#selector(closeDialog(_:)),
for: .touchUpInside
)
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!
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)
}
}