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)
}
}
Related
I have a view that is placed as a subview of navigationController in order to fill the whole display. On this view I have a subview that has two buttons. "Remove and Done". and then it also has a datepicker. The datePicker works, however, the Remove and Done buttons are not firing the action functions.
The buttons:
var setButton: UIButton = {
var button = UIButton(type: .system)
button.setTitle("Done", for: .normal)
button.tintColor = .white
button.addTarget(self, action: #selector(handleReminderSetBtn), for: .touchUpInside)
return button
}()
var cancelButton: UIButton = {
var button = UIButton(type: .system)
button.setTitle("Remove", for: .normal)
button.tintColor = .white
button.addTarget(self, action: #selector(handleReminderCancelBtn), for: .touchUpInside)
return button
}()
The main blackView that is in the navigationController:
viewOverLay.addSubview(cardreminder1)
viewOverLay.frame = CGRect(x: 0, y: 0, width: screenSize.width, height: screenSize.height)
viewOverLay.backgroundColor = UIColor.black.withAlphaComponent(0.3)
self.navigationController?.view.addSubview(viewOverLay)
CardReminder1 is the UIView on which I have two buttons.
I reckon there is some issue with the target in the addTarget method of the two buttons. What could be the issue?
You shouldn't initialize setButton and cancelButton in that way.
Quoting the Apple documentation from Setting a Default Property Value with a Closure or Function:
If you use a closure to initialize a property, remember that the rest of the instance has not yet been initialized at the point that the closure is executed. This means that you cannot access any other property values from within your closure, even if those properties have default values.
moreover:
You also cannot use the implicit self property, or call any of the instance’s methods, hence the problem is here:
addTarget(self...)
so to fix the issue you should move the buttons initialization (or move the addTarget) after your CardReminder1 is fully initialized.
I just had a similar issue. Along with the other answer posted, making the property lazy worked in my case as well. It depends on when you first try to access that property, but in my case I only accessed it after initialization was complete, so making the property lazy worked perfectly.
For example in your case, the following might work:
lazy var setButton: UIButton = {
var button = UIButton(type: .system)
button.setTitle("Done", for: .normal)
button.tintColor = .white
button.addTarget(self, action: #selector(handleReminderSetBtn), for: .touchUpInside)
return button
}()
lazy var cancelButton: UIButton = {
var button = UIButton(type: .system)
button.setTitle("Remove", for: .normal)
button.tintColor = .white
button.addTarget(self, action: #selector(handleReminderCancelBtn), for: .touchUpInside)
return button
}()
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)
}
}
I am trying to create a generic button creation function into which I pass a closure that represents the action that results as a result of clicking on the button. My code is below. However, I get the following error:
Argument of #selector cannot refer to property. Any suggestions for a workaround ? I don't want to write separate functions for which everything else is the same except for the target action.
func myButton(textColor tColor:UIColor , title:String,
_ buttonFcn: (UIButton) -> Void,
titleSize:CGFloat=30) -> UIButton {
let newButton = UIButton(type: .System)
let bgColor = UIColor(red:204/255, green:204/255, blue:204/255, alpha:1.0)
newButton.backgroundColor = bgColor
newButton.setTitle(title, forState: .Normal)
newButton.setTitleColor(tColor, forState: .Normal)
newButton.titleLabel?.font = newButton.titleLabel?.font.fontWithSize(titleSize)
newButton.addTarget(self, action:#selector(buttonFcn),
forControlEvents:
UIControlEvents.TouchUpInside)
return newButton
}
The problem is that the target-action mechanism is an Objective-C mechanism, and therefore is predicated on the notion that the action selector is a method of an object. You need, therefore, to have some NSObject-based object that has this function as a method, and which can then serve as the target.
Thus, if what differs in every case is the target and the action, what you need to pass is a reference to the target along with the selector string. Swift will squawk at this, but if you know how to form a selector string correctly you can certainly get away with it; you just won't be able to use the #selector syntax, and so you will risk crashing if you form the selector string incorrectly. But it's the kind of thing we used to do all the time in the old Objective-C days, so go right ahead if that's your aim.
Totally artificial but working example:
func buttonMaker(target:NSObject, selectorString:String) -> UIButton {
let b = UIButton(type:.system)
b.setTitle("Testing", for: .normal)
b.addTarget(target, action: Selector(selectorString), for: .touchUpInside)
b.sizeToFit()
return b
}
And here's how to call it from a view controller:
func doButton(_ sender:Any) {
print("ha!")
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let b = buttonMaker(target:self, selectorString:"doButton:")
b.frame.origin = CGPoint(x:100, y:100)
self.view.addSubview(b)
}
And when we tap the button, we don't crash (rather, we print "ha"), because I know how to make selector strings correctly. But, as you can see, to accomplish this I had to give up the use of #selector altogether, so safety is out the window. If I had written my selector string incorrectly — for instance, if I had spelled it wrong, or omitted the colon — we'd have crashed on the button tap, just like we used to all the time before Swift #selector and Objective-C #selector were invented.
If your deployment target is iOS 14 or later, you can use the addAction method instead of addTarget. The addAction method lets you use a closure instead of a selector:
func myButton(
textColor: UIColor,
title: String,
titleSize: CGFloat = 30,
_ handler: #escaping (UIButton) -> Void
) -> UIButton {
let button = UIButton(type: .system)
button.backgroundColor = UIColor(red: 204/255, green: 204/255, blue: 204/255, alpha: 1.0)
button.setTitle(title, for: .normal)
button.setTitleColor(textColor, for: .normal)
button.titleLabel?.font = button.titleLabel?.font.withSize(titleSize)
let action = UIAction { action in
guard let button = action.sender as? UIButton else { return }
handler(button)
}
button.addAction(action, for: .touchUpInside)
return button
}
iOS 14 was released on 2020-09-16 and supports iPhone 6S and later devices.
I have a UIButton that I created programmatically. I have added a target to it, but it doesn't seem to be running properly. Here is my code (This is a custom class of UIView):
override init(frame: CGRect) {
super.init(frame: frame)
print("targets:")
print(clickButton.allTargets())
clickButton.addTarget(self, action: #selector(self.clickPicture), for: .touchUpInside)
print("targets:")
print(clickButton.allTargets())
}
This is what prints as a result:
As you can see, adding a target to my button does not make a difference. Here is the clickPicture function:
func clickPicture() {
print("clickpicture")
}
Again, this does not print. Does anybody know how to fix this error? Thanks!
Edit:
Definition for clickButton (in my custom class):
var clickButton = UIButton()
Other properties defined in the init:
clickButton.frame.size = CGSize(width: 100, height: 100)
clickButton.layer.cornerRadius = clickButton.frame.size.width / 2
clickButton.layer.borderColor = UIColor.white().cgColor
clickButton.layer.borderWidth = 2.5
clickButton.layer.backgroundColor = shadeColor.cgColor
clickButton.center.x = self.center.x
clickButton.center.y = self.frame.size.height - clickButton.frame.size.height// + 40
clickButton.addTarget(self, action: #selector(self.clickPicture), for: .touchUpInside)
self.addSubview(clickButton)
The button was not on top of the custom view when I instantiated it. I changed its position, and it worked fine.
You have to change this line
clickButton.addTarget(self, action: #selector(self.clickPicture), for: .touchUpInside)
to this
clickButton.addTarget(self, action: #selector(YourClassName.clickPicture), forControlEvents: UIControlEvents.TouchUpInside)
And the method should be
func clickPicture() {
print("clickpicture")
}
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();