How does UIButton addTarget self work?
addTarget:action:forControlEvents:
tells the control (answerButton
in this case) what method to call, and what object to call it on, when the user taps the button. Looking at your code in more detail:
answerButton.addTarget(self, action: "didPressAnswerButton:", forControlEvents: .TouchUpInside)
- When the user taps a button, the
TouchUpInside
event fires on theanswerButton
, and when that happens we want to invoke a methoddidPressAnswerButton:
on anAnswer
object - So, we need to tell
answerButton
what do do when this TouchUpEvent fires. You do this calling theaddTarget:action:forControlEvents
method on theanswerButton
- The
self
argument tells theanswerButton
what object to notify about the event: it is the target. In this context,self
is anAnswer
object. - The
"didPressAnswerButton:"
argument indicates what method the answerButton should call in response to the tap event: this is the action
This is the target-action mechanism of Objective-C/Cocoa. It's a very common pattern, it's worth it to read the linked documentation to learn a bit more about how it works. The key is that this is based on Objective-C* message passing: in the code above, "didPressAnswerButton:"
indicates a selector, which when paired with a target (self
), tells the answerButton how to send a "message" to the target when the user taps the button.
Also, note that when you are editing a storyboard and ctrl-drag from a button to your view controller and select a method, you are also setting up a target/action using this same mechanism. You select the target object by dragging to the view controller icon (or some other icon), and then you pick the action/selector when clicking on a method name in the popup.
* Target-Action was originally designed for Objective-C, but for the common case of implementing a view controller, you can assume Swift works the same way. Just note when reading documentation that Swift uses simple strings for actions, whereas Objective-C uses @selector(...)
.
Dynamic Creation UIButton AddTarget on UIView executed on UIViewController
In the method button.addTarget(self, action: #selector(optionClicked(_:)), for: .touchUpInside)
you need to provide pointer to the viewController which will receive the action.
The easiest way in your case would be to create lazy variable DrawerView with DrawerController on the init and use the drawer controller in the button action.
DrawerView.swift
class DrawerView: UIView {
private unowned let drawerController: DrawerController
init(drawerController: DrawerController) {
self.drawerController = drawerController
}
... wherever is your code placed ...
let menuOptions = ["Info", "Actions", "Users", "Patiens"]
menuOptions.forEach({
let button = UIButton()
button.setTitle($0, for: .normal)
if $0.contains(menuOptions[0]) {
button.style(with: .filled)
} else {
button.style(with: .outlined)
button.addTarget(drawerController, action: #selector(DrawerController.optionClicked(_:)), for: .touchUpInside)
actionStackView.addArrangedSubview(button)
}
})
....
}
It's important you use unowned
or weak
to prevent retain cycles and memory leaks
To create the DrawerView you can use a lazy variable:
lazy var shareView: DrawerView = DrawerView(drawerController: self)
lazy will allow you to use self, you can also use optional and create the variable later, but that's basically what lazy does.
Hope it helps!
UIButton add target without sender
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
//...
}
UIButton addTarget:action:forControlEvents: not working using itself as a target?
Its not working bcoz you are adding self as target. You need to add Your ViewController as target on which you button is added.
[self addTarget:viewController action:@selector(onClick:forEvent:) forControlEvents:UIControlEventTouchUpInside];
addTarget:self versus addTarget:nil
If you add self or any other object as the target for an action message the message will be sent to exactly this object.
Adding nil as a target means that the actual target will be searched at runtime when the message is triggered. The lookup starts at the first responder object and continuous from there along the responder chain, that is by trying the object returned by the nextResponder method until an object is found that implements this method. Take a look at the event handling guide for more information on the exact lookup order.
Action of UIButton doesn't work if the button is initialized with let and the addTarget in it in custom view, but it works in UIViewController
self
inside that block actually is the block, not the view(you can see it using print(self)
), probably because it's not yet initialised(same reason why you can't use self before super.init
), that's why I usually add targets/delegates outside of the initialisation block. With lazy var
self is already initialised, so no problem should be there.
I'm not sure why it works at all, seems kind of magic for me, and I wouldn't depend on it.
Anyway, it works fine with both custom ViewController and custom View in my case: I've added CustomView
as subview to ViewController
in the storyboard.
class ViewController: UIViewController {
private let usernameButton: UIButton = {
let button = UIButton(type: .system)
button.setTitleColor(.black, for: .normal)
button.setTitle("someTitle", for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 13)
button.addTarget(self, action: #selector(didTapUsername), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.insertSubview(usernameButton, at: 0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
usernameButton.frame = view.bounds
}
@objc
func didTapUsername() {
print("ViewController", #function)
}
}
class CustomView: UIView {
private let usernameButton: UIButton = {
let button = UIButton(type: .system)
button.setTitleColor(.black, for: .normal)
button.setTitle("someTitle", for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 13)
button.addTarget(self, action: #selector(didTapUsername), for: .touchUpInside)
return button
}()
override func awakeFromNib() {
super.awakeFromNib()
addSubview(usernameButton)
}
override func layoutSubviews() {
super.layoutSubviews()
usernameButton.frame = bounds
}
@objc
func didTapUsername() {
print("CustomView", #function)
}
}
This shouldn't do differently for UIViewController
/UIView
, check out your logic
Related Topics
Find Delegate in a Swift Array of Delegates
Swift: How to Change Detailview Depending on Sidebar Selection State with Swiftui 4
Swift3: Live Uilabel Update on User Input
I Cannot Access My Array Objects Anywhere, How to Access Them in Swift
Admob Interstitial Alway Returns False
Xcode Constraint Break When Switching Between Text Fields Without Dismissing Keyboard
How to Position UI Elements on Background Image Relative to the Image
Mapbox Navigation Pod Unable to Install with Objectivec Where I am Using Xcode 9.2
Snapshot of Swiftui View Is Partially Cut Off
Why Print() Is Printing My String as an Optional
Delegates: Pass Data Without Segue
Replay Kit Not Working iPad iOS11 Bug
How to Authorize Twitter with Swifter
Attempting to Segue from Objective-C to Swift Vc