Programmatically created custom view with button target - ios

I'm having an custom UIView subclass which has some UIButtons appended. For every button there is an target action defined on the UIViewController which is including the custom UIView.
Somehow the TouchUpInside event is never being caught within the custom view including ViewController. Since UIViews are part of the UIResponderChain I wonder why events are not being fired? Does it make any sense to delegate the target action or should this be done another way?
Custom UIView Subclass: CustomView
let button = UIButton(frame: CGRectMake(10.0, 10.0, 100.0, 100.0))
button.addTarget(self.delegate, action: "buttonTapped:", forControlEvents: .TouchUpInside)
ViewController
// Include the custom view
let customView = CustomView()
customView.delegate = self
self.view.addSubview(customView)
...
func buttonTapped(sender: AnyObject!) {
// Doesn't get called
}

Do you set userInteractionEnabled to YES on your custom view? It's NO by default on views that are not descended from UIResponder (mostly buttons).
If a view's userInteractionEnabled = NO, it will ignore touch events.

I assume self.delegate is nil when you adding target...
Few solutions:
initialise custom view with delegate (create custom function initWithFrame:delegate)
override setDelegate function for custom view, and add target to button there;
make button variable public, and manually add target to it in your external class

Unfortunately I was missing to declare the frame rect for the buttons - so buttons are being displayed, but they're indeed not clickable.

Related

Link an #IBAction to a button in a .xib view from a ViewController

I have created a .xib file for a view that is going to be repetitive in my iOS app, inside of which there is a UIButton.
I have included that .xib view inside multiple UIViewControllers in my storyboard. I would like to link an #IBAction and an #IBOutlet to the button inside my .xib view that is specific to each UIViewController. In other words, I want every UIViewController to completely manage and handle the UIButton that is inside the .xib view.
Any idea if the above is feasible?
There are a couple of ways to do what you want.
The way I would do it is to give your custom view a closure that is run when the IBAction method is triggered. And each view controller that loads the view from the xib can pass in the closure to the view and the action will run when the button is clicked.
So here's the best solution I came up with so far.
Inside my .xib, I link the button to an #IBAction that is empty.
Again inside my .xib, I created a protocol with a single method that I will call inside the #IBAction created in step (1.)
The #IBAction will run the protocol method every time it is called, so every time the button is clicked.
Implement the protocol stub in every ViewController that needs to handle the #IBAction, and make sure to link that ViewController to the .xib using the protocol created in step (2.)
In your ViewController witch contains the xib view, just asign an action to the button inside xib view
class YourViewController:UIViewController{
override func viewDidLoad(){
//ListTitleView : a xib view, action button witch called theManageButton is inside it.
let theListTitleView = ListTitleView.init(frame:CGRect.init(x: 0, y: 0, width: self.view.frame.width, height: 100))
//Add action to theManageButton
theListTitleView.theManageButton.addTarget(self, action: #selector(yourFunction(Sender:)), for: .touchUpInside)
}
func yourFunction(Sender:UIButton){
//...Do something here
}
}

iOS11 UIBarButtonItem action not get called

I used Xcode9 Beta6 to build the project, the action was called correctly on iOS10 device, however it is not work on iOS11 device.
In My project, there are some viewControllers have a UIToolBar on the top, and the toolBar contains some UIBarButtonItems.
There is one this kind of viewController, whose UIBarButtonItem action is not called when I tap the UIBarButtonItem. I can see the tapping animation (the icon become dim first and back to normal after finger released)
At the end of viewDidLoad, I print the info of toolbar.items to indicate that target action are set properly.
Debug Output
In my case I was setting up the button and instantiating it as a property of the vc
class myVC: UIViewController {
let closeBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(close(_:)))
}
If I moved this to ViewDidLoad it resolved the problem
class myVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let closeBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(close(_:)))
}
}
I solved this problem by removing a current gesture recognizer from my view and adding a new one. Than I opened the connections inspector of my view and add gestureRecognizer connection to my gesture recognizer.
Apple has confirmed this bug.
My temporary solution is changing the gesture recognizer area by removing the overlap area, so that the tap gesture won't block the tap event on UIBarButtonItem.
It is happening only for iOS 11 and when custom UIView used for rightBarButtonItem (or left also). If you are using UIBarButtonItem then it will work fine.
There is 0 width of this custom bar item, so we need to set it to something.
In viewDidLoad of this controller you can add code, you can replace 100 to something what will work for you:
if let view = self.navigationItem.rightBarButtonItem?.customView {
view.widthAnchor.constraint(equalToConstant: 100).isActive = true
}
At least as an easy temporary solution it is fine.
In my case the problem was a gestureRecognizer added to the whole main view (in order to close the keyboard) like this:
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(closeKeyboard)];
[self.view addGestureRecognizer:gesture];
That gesture recognizer overrides the tap on the UIBarButtonItem, thus I solved by creating a new subview placed immediately below the navigation bar and assigning the gesture recognizer to that view and not to the whole main view.
Mark the method you are targeting at with #objc.

Ios - action on button in nib file

Good evening,
I'm wondering if displaying a nib file as a subview is the more standardized way of displaying a subview when compared to hiding and unhiding a view of the same class.
Also,
How would i be able to set an action on the buttons in the nib file?
let test = xWork.loadViewFromNib()
test.center = view.center
self.view.addSubview(test)
Currently doing the above. Even though my xWork nib has a class with an outlet for a button, i'm unsure as to how to set an action to it.
Thank you
Please clarify your first question. As for your second question, how to set the action of a button in a nib, try this:
In your nib, create a callback closure:
var onClickCallback: (Void -> Void)?
and in your button's IBAction, call the closure
#IBAction func buttonAction(sender: IBAction) {
onClickBallback?()
}
Then, where you use your nib, set the button callback:
let test = xWork.loadViewFromNib()
test.center = view.center
self.view.addSubview(test)
test.onClickCallback = {
print("Button clicked!")
// Try this to dismiss the view.
test.removeFromSuperview()
}
If your nib file owner is its super view(or view controller),just CTRL+DRAG an target-action.

selector is not called 2 tapgesture

I have added a UITapGestureRecognizer to a view, but when I click it the method is not being called.
func addTapGestuere(uiview: UIView) {
let tapGestureRecognizer:UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: Selector("cardTapped:"))
uiview.addGestureRecognizer(tapGestureRecognizer)
}
I run this on the viewDidload
self.addTapGestuere(self.Card1View)
self.addTapGestuere(self.Card2View)
I put a break point on the method
cardTapped(recognizer: UITapGestureRecognizer) {
}
but when I click on the image the method isnt called. I have user interaction enabled for all the views.
Be careful about the following:
The UIView you are trying to add the UITapGestureRecognizer has userInteractionEnabled set to true:
self.view.userInteractionEnabled = true
The UIView you are trying to get the tap to has no other views covering it. Use the View Debugger to confirm this.
Most importantly, make sure you are adding the UITapGestureRecognizer to the correct UIView. Adding it to self.view will add it to the UIViewController's view.
As a side note: You can add the UITapGestureRecognizer using the Inetrface Builder itself, then connecting IBAction for the same. Will reduce the probability of simple mistakes.

UITextField inputView - IBActions not fired

I want to replace the default keyboard of a UITextField with a custom keyboard. So I created a new subclass of a UIViewController with a xib-file (the other way like creating both files seperately and setting the File's Owner doesn't work either).
Then I added a button to the KeyboardView and connected it to an IBAction. After that I set the textfields inputView to the new view like this:
override func viewDidLoad() {
let keyboardVC = KeyboardViewController()
textField.inputView = keyboardVC.view
keyboardVC.view.autoresizingMask = .FlexibleHeight
keyboardVC.delegate = textField
}
It's working and the custom keyboard shows up, but if I touch the button, the IBAction is not called. What's the problem in my setup? (I checked some examples and they all do it the same way).
UPDATE:
I now removed the ViewController and subclassed the UIView. Now the actions are working. Why isn't it working with a ViewController?
Since no one holds the UIViewController- there is no reference to it after the viewDidLoad() ended, it is released from the memory.
When the button is pressed, the view controller that should response to the action is not exist -> you are holding only the view of the view controller as the textField.inputView.

Resources