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)
}
}
Related
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()
here is my button object
let loginRegisterButton:UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = UIColor(r: 50 , g: 80, b: 130)
button.setTitle("Register", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitleColor(.white, for: .normal)
button.addTarget(self, action:#selector(handleRegister), for: .touchUpInside)
return button
}()
and here is my function
func handleRegister(){
FIRAuth.auth()?.createUser(withEmail: email, password: password,completion: { (user, error) in
if error != nil
{ print("Error Occured")}
else
{print("Successfully Authenticated")}
})
}
I'm getting compile error, if addTarget removed it compiles successfully
Yes, don't add "()" if there is no param
button.addTarget(self, action:#selector(handleRegister), for: .touchUpInside).
and if you want to get the sender
button.addTarget(self, action:#selector(handleRegister(_:)), for: .touchUpInside).
func handleRegister(sender: UIButton){
//...
}
Edit:
button.addTarget(self, action:#selector(handleRegister(_:)), for: .touchUpInside)
no longer works, you need to replace _ in the selector with a variable name you used in the function header, in this case it would be sender, so the working code becomes:
button.addTarget(self, action:#selector(handleRegister(sender:)), for: .touchUpInside)
Try this with Swift 4
buttonSection.addTarget(self, action: #selector(actionWithParam(_:)), for: .touchUpInside)
#objc func actionWithParam(sender: UIButton){
//...
}
buttonSection.addTarget(self, action: #selector(actionWithoutParam), for: .touchUpInside)
#objc func actionWithoutParam(){
//...
}
Try this
button.addTarget(self, action:#selector(handleRegister()), for: .touchUpInside).
Just add parenthesis with name of method.
Also you can refer link : Value of type 'CustomButton' has no member 'touchDown'
let button: UIButton = UIButton()
button.setImage(UIImage(named:"imagename"), for: .normal)
button.addTarget(self, action:#selector(YourClassName.backAction(_sender:)), for: .touchUpInside)
button.frame = CGRect.init(x: 5, y: 100, width: 45, height: 45)
view.addSubview(button)
#objc public func backAction(_sender: UIButton) {
}
Try with swift 3
cell.TaxToolTips.tag = indexPath.row
cell.TaxToolTips.addTarget(self, action: #selector(InheritanceTaxViewController.displayToolTipDetails(_:)), for:.touchUpInside)
#objc func displayToolTipDetails(_ sender : UIButton) {
print(sender.tag)
let tooltipString = TaxToolTipsArray[sender.tag]
self.displayMyAlertMessage(userMessage: tooltipString, status: 202)
}
In swift 3 use this -
object?.addTarget(objectWhichHasMethod, action: #selector(classWhichHasMethod.yourMethod), for: someUIControlEvents)
For example(from my code) -
self.datePicker?.addTarget(self, action:#selector(InfoTableViewCell.datePickerValueChanged), for: .valueChanged)
Just give a : after method name if you want the sender as parameter.
Try this with Swift 3
button.addTarget(self, action:#selector(ClassName.handleRegister(sender:)), for: .touchUpInside)
Good luck!
The poster's second comment from September 21st is spot on. For those who may be coming to this thread later with the same problem as the poster, here is a brief explanation. The other answers are good to keep in mind, but do not address the common issue encountered by this code.
In Swift, declarations made with the let keyword are constants. Of course if you were going to add items to an array, the array can't be declared as a constant, but a segmented control should be fine, right?! Not if you reference the completed segmented control in its declaration.
Referencing the object (in this case a UISegmentedControl, but this also happens with UIButton) in its declaration when you say .addTarget and let the target be self, things crash. Why? Because self is in the midst of being defined. But we do want to define behaviour as part of the object... Declare it lazily as a variable with var. The lazy fools the compiler into thinking that self is well defined - it silences your compiler from caring at the time of declaration. Lazily declared variables don't get set until they are first called. So in this situation, lazy lets you use the notion of self without issue while you set up the object, and then when your object gets a .touchUpInside or .valueChanged or whatever your 3rd argument is in your .addTarget(), THEN it calls on the notion of self, which at that point is fully established and totally prepared to be a valid target. So it lets you be lazy in declaring your variable. In cases like these, I think they could give us a keyword like necessary, but it is generally seen as a lazy, sloppy practice and you don't want to use it all over your code, though it may have its place in this sort of situation. What it
There is no lazy let in Swift (no lazy for constants).
Here is the Apple documentation on lazy.
Here is the Apple on variables and constants. There is a little more in their Language Reference under Declarations.
Instead of
let loginRegisterButton:UIButton = {
//... }()
Try:
lazy var loginRegisterButton:UIButton = {
//... }()
That should fix the compile error!!!
the Demo from Apple document. https://developer.apple.com/documentation/swift/using_objective-c_runtime_features_in_swift
import UIKit
class MyViewController: UIViewController {
let myButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
override init(nibName nibNameOrNil: NSNib.Name?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// without parameter style
let action = #selector(MyViewController.tappedButton)
// with parameter style
// #selector(MyViewController.tappedButton(_:))
myButton.addTarget(self, action: action, forControlEvents: .touchUpInside)
}
#objc func tappedButton(_ sender: UIButton?) {
print("tapped button")
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
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)
When I first run my app, I retrieve a number from my server and display it for my UIButton label. Think of this as a notification number displayed on a red UIButton.
When I remove a notification within the app, I want my UIButton label decrement by 1. I am able to get the decremented number from the server after I delete a notification, but I can't display this new number on the UIButton. The button always displays the number when the app is first fired.
I call makeButtonView() method after I remove a notification to update the UIButton
func makeButtonView(){
var button = makeButton()
view.addSubView(button)
button.tag = 2
if (view.viewWithTag(2) != nil) {
view.viewWithTag(2)?.removeFromSuperview()
var updatedButton = makeButton()
view.addSubview(updatedButton)
}else{
println("No button found with tag 2")
}
}
func makeButton() -> UIButton{
let button = UIButton(frame: CGRectMake(50, 5, 60, 40))
button.setBackgroundImage(UIImage(named: "redBubbleButton"), forState: .Normal)
API.getNotificationCount(userID) {
data, error in
button.setTitle("\(data)", forState: UIControlState.Normal)
}
button.addTarget(self, action: "targetController:", forControlEvents: UIControlEvents.TouchUpInside)
return button
}
Use this code for Swift 4 or 5
button.setTitle("Click Me", for: .normal)
I need more information to give you a proper code. But this approach should work:
lazy var button : UIButton = {
let button = UIButton(frame: CGRectMake(50, 5, 60, 40))
button.setBackgroundImage(UIImage(named: "redBubbleButton"), forState: .Normal)
button.addTarget(self, action: "targetController:", forControlEvents: UIControlEvents.TouchUpInside)
return button
}()
func makeButtonView(){
// This should be called just once!!
// Likely you should call this method from viewDidLoad()
self.view.addSubview(button)
}
func updateButton(){
API.getNotificationCount(userID) {
data, error in
// be sure this is call in the main thread!!
button.setTitle("\(data)", forState: UIControlState.Normal)
}
}
There have been some updates since Swift 4. This works for me:
self.button.setTitle("Button Title", for: UIControl.State.init(rawValue: 0))
Replace button with your IBOutlet name. You can also use a variable or array in place of the quoted text.
It's fairly simple ...
import UIKit
class ViewController: UIViewController {
#IBOutlet var button: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
button.setTitle("hello world", forState: UIControlState.Normal)
}
}
I believe if you set the state to normal, the value will propagate by default to other states so long as you haven't explicitly set a title for those states.
Said differently, if you set it for normal, it should also display this title when the button enters additional states
UIControlState.allZeros
UIControlState.Application
UIControlState.Disabled
UIControlState.Highlighted
UIControlState.Reserved
UIControlState.Selected
Lastly, here's Apple's documentation in case you have other questions.
Since your API call should be running on a background thread you need to dispatch your UI update back to the main thread like this:
DispatchQueue.main.async {
button.setTitle(“new value”, forState: .normal)
}
After setting the title, just a simple redraw of the button will do:
button.setNeedsDisplay();