Call selector from nother class - Swift 3 - swift

I'm creating an UIbutton from "Utils_class", and return it to my "getBtsDetails" class.
I successfully achieve to call "buttonAction" function implemented into "getBtsDetails", but then it return an error
"Unexpectedly found nil while unwrapping an Optional value"
getBtsDetails :
let Utils_Class = Utils()
var labelY : Int = 90
override func viewDidLoad() {
super.viewDidLoad()
WS_Class.GetDiplomaSubject(id: Int(self.BTS_id)!) { number, responseObject, error in
if((error) == nil) {
#let's create an UIbutton from Utils_class which gonna call the buttonAction below
self.scrollview.addSubview(self.Utils_Class.createButton(text: "Calcul", buttonY:self.labelY+30))
self.scrollview.contentSize.height = CGFloat(self.labelY+40)
}
}
}
#objc func buttonAction(_ sender:UIButton!)
{
#the error occurs here
print(self.Utils_Class.getTextFields(view : self.scrollview)/1)
}
And my second class, Utils :
public func createButton(text: String!, buttonY: Int!) -> UIButton {
helpClasses = GetBTSDetails.init()
let button = UIButton(type: UIButtonType.system) as UIButton
button.frame = CGRect(x:0, y:buttonY, width:self.screenWidth/5, height:15)
button.backgroundColor = UIColor.lightGray
button.setTitle(text, for: UIControlState.normal)
button.tintColor = UIColor.black
button.addTarget(btsClasse, action: #selector(btsClasse.buttonAction(_:)), for: .touchUpInside)
return button
}
var btsClasse: GetBTSDetails!

Ok, i finally succeed, by simply creating a new instance of my class and replacing :
var btsClasse: GetBTSDetails!
by
var btsClasse = GetBTSDetails()
This way, my class "GetBTSDetails", all functions, variables are already initialized allowing myself to call the UIButton action.

Because you didn't init it
var btsClasse: GetBTSDetails!
so when target runs
button.addTarget(btsClasse, action: #selector(btsClasse.buttonAction(_:)),for: .touchUpInside
what in buttonAction will be nil , so you have to linkbtsClasse object to the presented instance that you reference the selector from

Related

Argument of '#selector' does not refer to an '#objc' method, property, or initializer in Swift button target

i want to send parameter to my details func with selector but it does not work, how can i do this?
btnDetails.addTarget(self, action:#selector(goToDetailsFromMap(mid:id)), for: .touchUpInside)
You can create a custom button:
class CustomButton: UIButton {
var someId: Int?
...
}
Then in your viewController
...
btnDetails.someId = ...
btnDetails.addTarget(self, action:#selector(didTapCustomButton(_:)), for: .touchUpInside)
...
#objc func didTapCustomButton(_ sender: CustomButton) {
if let id = sender.someId {
//Do something with id
}
}
adding selector to button with code using swift 5.0
myButton.addTarget(self, action:#selector(myButtonTapped), for: .touchUpInside)
function declaration
#objc func myButtonTapped() {
//do stuff here
}

How to fix UIButton not responding to touch?

I am working an app with quizzes, have 4 UIButtons (custom type) on subview which is custom view and none of the buttons is recognising the action even though I set the target. I have to do it programatically.
I have tried setting isUserInteractionEnabled but it is not working. Buttons are on the view, respecting the bounds.
Here, viewDidLoad from InitialViewController:
override func viewDidLoad()
{
super.viewDidLoad()
self.questionView?.firstanswer!.isEnabled = true
self.questionView?.firstanswer!.addTarget(self, action: #selector(self.answerTheQuestion), for: UIControl.Event.touchUpInside)
self.questionView?.secondanswer!.addTarget(self, action: #selector(self.answerTheQuestion), for: UIControl.Event.touchUpInside)
self.questionView?.thirdanswer!.addTarget(self, action: #selector(self.answerTheQuestion), for: UIControl.Event.touchUpInside)
self.questionView?.fourthanswer!.addTarget(self, action: #selector(self.answerTheQuestion), for: UIControl.Event.touchUpInside)
}
Here is the action function:
#objc func answerTheQuestion(_sender:UIButton) {
print("aa")
if let questionsAnswers = self.quizzes?.quizzes[self.randomNumber!]["questions"] as? [[String:Any]] {
if (questionsAnswers[self.randomQuestion!]["question"] as? String) != nil {
if let correctAnswer = questionsAnswers[self.randomQuestion!]["correct_answer"] as? String {
if _sender.titleLabel?.text == correctAnswer {
_sender.backgroundColor = UIColor.green
} else {
_sender.backgroundColor = UIColor.red
}
}
}
}
}
View and buttons are added normally, only tap is not working.
It should go red when the answer is wrong, and green when it is right. Questions and everything else is added normally, but it even won't print "aaa" in the beginning of the target function. Pls help, thanks!!

Passing parameters in a clickable UIabel

I'm trying to make a clickable UILabel by following with this code:
let tap_plato = UITapGestureRecognizer(target: self, action: #selector(ViewController.ale_plato1))
plato1.isUserInteractionEnabled = true
plato1.addGestureRecognizer(tap_plato)
...
#objc
func ale_plato1(sender: UITapGestureRecognizer){
let label = sender.view
print ("tapped!")
}
This works well. But I want to pass parameters to the function. Something like this:
let tap_plato = UITapGestureRecognizer(target: self, action: #selector(MenuController.ale_plato1("parameter")))
plato1.isUserInteractionEnabled = true
plato1.addGestureRecognizer(tap_plato)
#objc
func ale_plato1(sender: UITapGestureRecognizer, parameterRecived: String){
}
But I don't know how do that in swift 3...
Any help? Thanks you
Wherever you make your multiple UILabel set each label to a different tag.
for i in 0..<3 {
let label = UILabel()
label.tag = i
let tap = UITapGestureRecognizer(target: self, action: #selector(tap))
label.isUserInteractionEnabled = true
label.addGestureRecognizer(tap_plato)
}
#objc func tap(sender: UITapGestureRecognizer) {
let label = sender.view as! UILabel // if you are SURE that your tap will be a UILabel
if(label.tag == 0) {
label.text = "This is label 0"
if(label.tag == 1) {
label.text = "This is label 1"
}
}
Threw this up in a jiffy, hop over the syntax issues if there are any.
Here we can create labels with tags and apply the same tap to them. From there, we can check what tag it is inside the tap method and do something from there.
You could use the tag attribute for Int values, but if you want anything other than that:
Create a custom UILabel Class with a value variable.
class LabelWithValue : UILabel {
var value : String = ""
}
Then you can use it as a normal Label
let label = LabelWithValue()
label.text = "bla"
label.value = "Anything"
label.isUserInteractionEnabled = true
label.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleGetText)))
Then you can get it's text and it's value.
#objc func handleGetText(_ sender : UITapGestureRecognizer) {
if let label = sender.view as? LabelWithValue {
print(label.text)
print(label.value)
}
}

Custom class to create an Alert with Buttons. How to add a completition handler?

I was trying to create a custom alert and I am getting mad trying to implement a completition handler on the buttons. I have tried a few things, the last, create func array to pass in the selector addTarget function of the UIButton, but not working. (where the ****** are)
The issue: "Argument of #selector does no refer to #obc method, property or initializer"
The difficult coding part I can't do is to configure the selector with some code I receive from my view controller where I create an object with the class below.
class Alert: NSObject {
func showAlert(){
if let window = UIApplication.shared.keyWindow {
//configure some constraints and animations
}
var buttons: [UIButton] = []
var buttonsFunc: [() -> Void ] = []
func addNewButton(title: String, handler: #escaping () -> Void) {
buttons.append(createButton(title: title))
buttonsFunc.append {
self.dismissAlert()
handler()
}
}
func setupButtons() {
for (index, button) in buttons.enumerated() {
boxView.addSubview(button)
//Here is the problem ***************************
button.addTarget(self, action: #selector(buttonsFunc[index]), for: .touchUpInside)
//More constraints(not important)
button.centerXAnchor.constraint(equalTo: boxView.centerXAnchor).isActive = true
button.widthAnchor.constraint(equalTo: (button.titleLabel?.widthAnchor)!).isActive = true
button.heightAnchor.constraint(equalToConstant: 25).isActive = true
}
}
func dismissAlert(){
//Animation to dismiss my alert
}
Other functions:
//Even if its not important the function i use to create the button
func createButton(title: String) -> UIButton {
let button = UIButton(type: .system)
button.backgroundColor = .clear
button.setTitle(title, for: .normal)
button.titleLabel?.sizeToFit()
button.setTitleColor(uPBlue, for: .normal)
button.titleLabel?.font = UIFont(name: uPFont, size: 20)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}
Any ideas how to fix this?
Or maybe a totally different way.
If it works i take it.
So a selector is just the name of a function. A closure by definition is an anonymous function so you can't quite do it this way.
Lets try another route, define a new function to give to the buttons:
private func buttonPressed(sender: UIButton) {
}
Then lets give the buttons this function instead of the closure:
...
button.addTarget(self, action: #selector(Alert.buttonPressed(sender:)), for: .touchUpInside)
...
Now we can take advantage of tuples here. Instead of having two separate arrays we'll to one array with an adhoc data structure for our buttons:actions:
// var buttons: [UIButton] = []
// var buttonsFunc: [() -> Void ] = []
// Becomes
var buttonActionArray: [(button: UIButton, action: () -> Void)] = []
Now lets implement buttonPressed(sender:)
private func buttonPressed(sender: UIButton) {
for buttonTuple in buttonActionArray {
if buttonTuple.button === sender {
buttonTuple.action()
}
}
}

Swift: "unrecognised selector sent to instance" both with/without delegate

** Everything described below was done programmatically. No use of StoryBoard **
In my ViewController I create a CustomCheckBoxContainer that contains buttons and labels. (labels not shown here!!) I've added actions to the buttons and as long as I address a function in a child (eg. CustomCheckBoxContainer and CustomCheckBox) the wether is shiny and bright. However, when trying to add an action that calls a function in ViewController, clouds start to form above my project.
Error message: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView buttonClicked3:]: unrecognized selector sent to instance ...'.
Button action:
newButton.addTarget(currentView, action: #selector(ViewController.buttonClicked3(_:)), forControlEvents: UIControlEvents.TouchUpInside)
Then realised you can't sent data or actions back upstream. So created protocol and delegate and rewritten the action to:
newButton.addTarget(self, action: #selector(delegate3.buttonClicked3(_:)), forControlEvents: UIControlEvents.TouchUpInside)
. Unfortunately, exact same result.
I'm completely stuck. What am I not seeing?
ViewController.swift
class ViewController: UIViewController, ButtonClicked3Delegate {
#objc func buttonClicked3(sender: UIButton) {
print ("Action received in the ViewController Class")
}
let userArray: [String] = ["one","two”]
override func viewDidLoad() {
super.viewDidLoad()
for item in userArray {
let customCheckBoxContainer = CustomCheckBoxContainer()
customCheckBoxContainer.showNewButton(..args.., currentView: UIView, ... args ...)
self.view.addSubview(customCheckBoxContainer)
}
}
}
CustomCheckBoxContainer.swift:
#objc protocol ButtonClicked3Delegate {
#objc func buttonClicked3(sender: UIButton)
//other data needed
}
class CustomCheckBoxContainer: UIView {
var newButton: CustomCheckBox!
var delegate3 : ButtonClicked3Delegate!
func showNewButton (... args... currentView: UIView, ... args ... ) {
newButton = CustomCheckBox (type: UIButtonType.Custom)
newButton.bounds = ...
newButton.center = ...
newButton.addTarget(newButton, action: #selector(CustomCheckBox.buttonClicked1(_:)), forControlEvents: UIControlEvents.TouchUpInside)
newButton.addTarget(self, action: #selector(CustomCheckBoxContainer.buttonClicked2(_:)), forControlEvents: UIControlEvents.TouchUpInside)
newButton.addTarget(self, action: #selector(delegate3.buttonClicked3(_:)), forControlEvents: UIControlEvents.TouchUpInside)
currentView.addSubview(newButton)
}
func buttonClicked2(sender:UIButton) {
print ("Action received in the CustomCheckBoxContainer Class")
}
}
CustomCheckBox.swift
class CustomCheckBox: UIButton {
func buttonClicked1(sender:UIButton) {
print ("Action was received in CustomCheckBox Class")
}
}
I would split it into two stages.
Link the button to a selector in the child view controller (the following is Swift 2.3 syntax) along the following lines:
button.addTarget(self, action #selector(self.buttonTapped()), forControlEvents: UIControlEvents.touchUpInside)
Then call the delegate method from within your implementation of buttonTapped(_:):
func buttonTapped() -> Void {
Self.delegate.buttonWasTapped()
...
}
[Incidentally, check also that the delegate is not nil. You've declared it as an implicitly unwrapped optional which is correct, but this means you need to set the delegate from the parent view controller.]