Swift 3 Method from Class not working with #selector syntax - swift

I feel as though I'm not understanding the Swift #selectors properly. I'm trying to connect a button to a method from another class.
I have a class to print the button when pushed:
class printThings {
#IBAction func printMe(_ sender: UIButton){
print("Button Pushed.")
}
}
And then the ViewController:
class ViewController : UIViewController {
override func ViewDidLoad(){
super.viewDidLoad()
//button setup here
let printMe = printThings()
button.addTarget(printMe, action: #selector(printMe.printMe(_:)), for: .touchUpInside)
//add button to subview
}
}
This never triggers the print statement in the class. I'm sure I'm missing something simple.
Thanks.

The problem is that printMe is a temporary, local variable:
let printMe = printThings() // local variable
button.addTarget(printMe, action: #selector(printMe.printMe(_:)), for: .touchUpInside)
// ... and then viewDidLoad ends, and `printMe` vanishes
So by the time you push the button, printMe has vanished; there is no one to send the button message to.
If you want this to work, you need to make printMe persist:
class ViewController : UIViewController {
let printMe = printThings() // now it's a _property_ and will persist
override func viewDidLoad(){
super.viewDidLoad()
//button setup here
button.addTarget(self.printMe, action: #selector(self.printMe.printMe(_:)), for: .touchUpInside)
//add button to subview
}
}

Related

How do I targeting selector with addTarget in another class

I try to understand how to control component form one class to another class but that's not working.
class VC:UIViewController {
override func viewDidLoad() {
let instance = Test()
self.view.addSubView(instance.button)
}
}
class Test:NSObject {
var button:UIButton!
override init() {
button = UIButton(frame:CGRect(x:0, y:0, width:100, height:40))
button.setTitle(("TEST", for:.normal)
button.addTarget(self, action: #selector(tapButton(_:)), for: .touchUpInside)
}
#objc func tapButton(_ sender:UIButton) {
print("TAP Button")
}
}
When I tapped the button, nothing happens !
I try to change
button.addTarget(self, action: #selector(tapButton(_:)), for: .touchUpInside)
with
button.addTarget(nil, action: #selector(tapButton(_:)), for: .touchUpInside)
That's not working !
How to resolve this problem ?
Thanks for your help.
You have 3 issues:
instance is a local variable, so it gets deallocated as soon as viewDidLoad() finishes. Make it a property of VC. The button only keeps a weak reference to the object, so when it is deallocated it becomes nil. In cases where the target is nil, UIKit will search up the responder chain for the action method. Since VC doesn't supply a tapButton method, nothing happens when the button is pressed.
You need to call super.init() so that self is available to be used with the button. self can't be created until all properties have been initialized. Because init is an override, you must call super.init() to initialize the properties that NSObject provides before you can use self in the button.
Your frame puts your button in an unaccessible region of the screen. I changed your frame to put the button inside the safe area of the iPhone 11. I also made the button .green so that I could see it.
class VC: UIViewController {
var instance = Test() // make instance a property
override func viewDidLoad() {
// let instance = Test() // this was a local variable that doesn't hang around
self.view.addSubview(instance.button)
}
}
class Test: NSObject {
var button: UIButton!
override init() {
super.init() // call this so that you can use self below
button = UIButton(frame:CGRect(x: 100, y: 100, width: 100, height: 40))
button.setTitle("TEST", for:.normal)
button.backgroundColor = .green
button.addTarget(self, action: #selector(tapButton(_:)), for: .touchUpInside)
}
#objc func tapButton(_ sender: UIButton) {
print("TAP Button")
}
// Add deinit to see when this object is deinitialized. When
// instance is local to viewDidLoad() this object gets freed
// when viewDidLoad() finishes.
deinit {
print("Oops, the Test object has been deinitialized")
}
}

Type has no member during Apple tutorial

I just started learning Swift. I am following this Apple tutorial but I was met with this error. I copied the exact code from the tutorial. Not sure where did I go wrong.
Error message:
"Type 'RatingControl' has no member 'ratingButtonTapped(button:)'"
//Mark: Private Methods
private func setupButtons() {
for _ in 0..<5 {
//Create the button
let button = UIButton()
button.backgroundColor = UIColor.red
//Add constraints
button.translatesAutoresizingMaskIntoConstraints = false
button.heightAnchor.constraint(equalToConstant: 44.0).isActive = true
button.widthAnchor.constraint(equalToConstant: 44.0).isActive = true
//Setup the button action
button.addTarget(self, action: #selector(RatingControl.ratingButtonTapped(button:)), for: .touchUpInside)
//Add the button to the stack
addArrangedSubview(button)
//Add the new button to the rating button array
ratingButtons.append(button)
}
}
Initial to get the button action the RatingControl.swift class will be like
import UIKit
#IBDesignable class RatingControl: UIStackView {
//MARK: Initialization
override init(frame: CGRect) {
super.init(frame: frame)
}
required init(coder: NSCoder) {
super.init(coder: coder)
}
//MARK: Button Action
#objc func ratingButtonTapped(button: UIButton) {
print("Button pressed")
}
}
Create a file named RatingControl.swift with code and run your project. This issue will be resolved.
if you already have the full class the just add #objc before the method ratingButtonTapped.
More info: You can download the full project of the tutorial you currently following. The download link is present at the bottom.
We truly need to see the whole RatingControl class in order give you a complete answer. However your problem based on the error is that you there is no function with the name ratingButtonTapped inside your RatingControl class. There should be a class like the following in order to register it as a target to a button.
Example of .addTarget usage:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//Setup the button action
let button = UIButton()
button.addTarget(self, action: #selector(ratingButtonTapped(_:)), for: .touchUpInside)
view.addSubview(button)
}
#objc func ratingButtonTapped(sender: UIButton) {
// Triggered when the button is pressed
}
}

how to add pan gesture on long pressing a button [duplicate]

I want to trigger two action on button click and button long click. I have add a UIbutton in my interface builder. How can i trigger two action using IBAction can somebody tell me how to archive this ?
this is the code i have used for a button click
#IBAction func buttonPressed (sender: UIButton) {
....
}
can i use this method or do i have to use another method for long click ?
If you want to perform any action with single tap you and long press the you can add gestures into button this way:
#IBOutlet weak var btn: UIButton!
override func viewDidLoad() {
let tapGesture = UITapGestureRecognizer(target: self, #selector (tap)) //Tap function will call when user tap on button
let longGesture = UILongPressGestureRecognizer(target: self, #selector(long)) //Long function will call when user long press on button.
tapGesture.numberOfTapsRequired = 1
btn.addGestureRecognizer(tapGesture)
btn.addGestureRecognizer(longGesture)
}
#objc func tap() {
print("Tap happend")
}
#objc func long() {
print("Long press")
}
This way you can add multiple method for single button and you just need Outlet for that button for that..
#IBOutlet weak var countButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
addLongPressGesture()
}
#IBAction func countAction(_ sender: UIButton) {
print("Single Tap")
}
#objc func longPress(gesture: UILongPressGestureRecognizer) {
if gesture.state == UIGestureRecognizerState.began {
print("Long Press")
}
}
func addLongPressGesture(){
let longPress = UILongPressGestureRecognizer(target: self, action: #selector(longPress(gesture:)))
longPress.minimumPressDuration = 1.5
self.countButton.addGestureRecognizer(longPress)
}
Why not create a custom UIButton class, create a protocol and let the button send back the info to delegte. Something like this:
//create your button using a factory (it'll be easier of course)
//For example you could have a variable in the custom class to have a unique identifier, or just use the tag property)
func createButtonWithInfo(buttonInfo: [String: Any]) -> CustomUIButton {
let button = UIButton(type: .custom)
button.tapDelegate = self
/*
Add gesture recognizers to the button as well as any other info in the buttonInfo
*/
return button
}
func buttonDelegateReceivedTapGestureRecognizerFrom(button: CustomUIButton){
//Whatever you want to do
}

I have custom uinavigation class. In that custom delegate method declared. How to access that method to view controller

#objc protocol MyDelegate {
func buttonAction()
}
class CustomNavigationBar: UINavigationController {
var delegte : MyDelegate?
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton.init(frame: CGRectMake(200, 10, 50, 30))
button.setBackgroundImage(UIImage(named: "a.png"), forState: .Normal)
button.addTarget(self, action: "testing", forControlEvents: .TouchUpInside)
self.navigationBar.addSubview(button)
}
func testing(){
self.delegte?.buttonAction()
print("Pavan")
}
If i press this button, testing is calling.
But in viewcontroller calling delegate method but giving error
class ViewController: UIViewController,MyDelegate{
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.title = "hi"
let vc = CustomNavigationBar()
vc.delegte = self
}
func buttonAction() {
print("Tupale")
}
would u mind to hint the error message?
updated:
In CustomNavigationBar class, you have to change var delegte : MyDelegate? to var delegte : UIViewController?.
then in ViewController class, you could set self which is an instance of UIViewController to the delegate of vc.
Have a try.

UIButton action generates a SIGABRT but no breakpoint triggers

I have this code to create a button on my storyboard
override func viewDidLoad() {
super.viewDidLoad()
...
signupButton.addTarget(self, action: "pressed:", forControlEvents: .TouchUpInside)
...
}
func pressed(sender:UIButton!) {
println("Pressed")
}
However, it throws a SIGABRT error when I press the button. So, I added a breakpoint on all exceptions. However, no breakpoint was ever it - it just threw the exception. How do I find out what's causing the error?
You have two issues here...
First: You placed the pressed function in the scope of viewDidLoad, when it needs to be outside of that function and in the scope of the class. Move the function pressed out of viewDidLoad().
Second: This is not what is causein the signal abort crash, but it is needed nonetheless. Add a semicolon to the action selector name.
signupButton.addTarget(self, action: "pressed:", forControlEvents: .TouchUpInside)
Or, if you don't need to pass any object to the function pressed, then change the function header to func pressed().
BASIC EXAMPLE: (tested and working in new project)
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let signupButton = UIButton(frame: CGRectMake(0, 0, 100.0, 100.0))
signupButton.setTitle("BUTTON", forState: UIControlState.Normal)
signupButton.addTarget(self, action: "pressed:", forControlEvents: .TouchUpInside)
self.view.addSubview(signupButton)
}
func pressed(sender:UIButton!) {
println("Button pressed")
}
}