** 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.]
Related
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
}
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
An image sends an action to an #objc C function when pressed:
let imageView = UIImageView()
imageView.tag = 3
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(ItemAction)))
imageView.image = UIImage(named: (itemDictionary[character.Equipment[imageView.tag]]!.image))
overlayView.addSubview(imageView)
this is the function that should be called:
#objc func ItemAction(sender: UIImageView!) {
print(sender.tag)
print("Item pressed from sender ")
}
the function runs, but when it comes to printing the sender tag I get an error message and the program quits:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITapGestureRecognizer tag]:
unrecognized selector sent to instance 0x600002b42a00'
The whole setup with a UIButton works.
How can I read the tag of an UIImageView in an external function?
here you go
#objc func itemAction(_ sender: UITapGestureRecognizer){
if let tag = sender.view?.tag{
print("ImageView tag \(tag)")
}
}
in your function change your method signature to
#objc func ItemAction(_ sender: UITapGestureRecognizer)
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()
}
}
}
Currently I have 2 classes: ViewController and class A. My objective is to add tap gesture recognizer to a UIView from class A. My ViewController:
class ViewController {
#IBOutlet var area: UIView!
func enterClassA() {
let classA = A(self.area)
//some processing
}
}
class A : UIGestureRecognizerDelegate {
private var currView: UIView!
init(newView: UIView) {
self.currView = newView
self.addTapGesture()
}
func addTapGesture() {
let tap = UITapGestureRecognizer(target: self.currView, action:#selector(tapDetected(_:)))
tap.delegate = self.currView as! UIGestureRecognizerdelegate? //***
self.currView.addGestureRecognizer(tap)
}
#objc func tapDetected(_ tapGesture: UIGesturerecognizer) {
print ("Tap detected!")
}
}
But now it's giving error: could not cast value of type 'UIView' to "UIGestureRecognizerDelegate". I tried modifying line *** to
tap.delegate = self
But it's still not working, showing error: unrecognized selector sent to instance. Deleting the line gives the same error. May I know if this is even doable or not (adding gesture recognizer from a different class)? If so then how should I approach it?
In this line
let tap = UITapGestureRecognizer(target: self.currView, action:#selector(tapDetected(_:)))
You say the gesture recognizer to search the tapDetected() function in the self.currView object. Try to change the target:
let tap = UITapGestureRecognizer(target: self, action:#selector(tapDetected(_:)))
And yes, it should be like that:
tap.delegate = self
Since your A class conforms to UIGestureRecognizerDelegate.