Issues using UITapGestureRecognizers in Interface Builder - objective-c

I'm attempting to use the UITapGestureRecognizer object that can be found in Interface Builder. I've dragged a single "UITapGestureRecognizer" from the object library to a single view xib. I then create an IBAction method from this tap gesture, for a simple test, I'm just printing an "NSLog" message to the console once there is a tap on the view. I've run this, and the tap method isn't being called. I right click the view in IB and I noticed that there is a warning "!" on the view's "Outlet Collections" I see:
Outlet Collections
gestureRecognizers - Tap Gesture Recognizer (!)
The warning states: UIView does not have an outlet collection named gestureRecognizers.
What do I need to do to remedy this?

Mr.Anonymous solution is correct.
No need to implement the delegate in the view controller or set it.
However, you should check that User interaction enabled is checked (in the properties window on the right), especially if you are attaching the recognizer to a label.

I think you have not wired the UITapGestureRecognizer properly to your code.
When you drop a UITapGestureRecognizer on your xib Xcode automatically makes the necessary referencing outlet connections.
You only need to create an IBAction method in your code and then wire it to the selector of the UITapGestureRecognizer placed in xib.
I have attached screenshots for ur reference.
Hope this helps!!

I had forgotten to check that User Interaction Enabled has to be checked for the views the gesture recognizer is added.

Two things to check: Does your view controller (the one that contains the UIView) implement the UIGestureRecognizerDelegate protocol?
Once it implements UIGestureRecognizerDelegate, make sure you've set the gesture recogniser's delegate property to the view controller. I used a storyboard to make the connection.
I do this and I don't get any errors (IOS 5.1, xCode 4.3).

I did this to add a Double Tap Gesture to my Navigation bar, completely in code and found it very easy to use ....
In view did load ...
//Add double tap gesture to Navbar
UITapGestureRecognizer* tapRecon = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(navigationBarDoubleTap:)];
tapRecon.numberOfTapsRequired = 2;
[self.navigationController.navigationBar addGestureRecognizer:tapRecon];
I then have a method ...
#pragma mark - Auto Refresh Method
- (void)navigationBarDoubleTap:(UIGestureRecognizer*)recognizer {
//Do Stuff Here
}
Maybe you could adapt this.
Plasma

Take a look at this.
Specifically the screen shot of the outlet setup. Hopefully that will help.

The warning shouldn't be an issue. It is only there because you can add multiple gesture recognizers as an IBOutlet Collection but this isn't required.
Turn on zombies in your scheme to get better error messages whilst debugging. In my case the View controller I was trying to message with my gesture handler wasn't being instantiated in Interface Builder.
Also, it's not necessary to implement the delegate protocol and it will work on any UIView.

Related

Same Viewcontroller pops multiple times

Required:
I want to enable iOS7 swipe to back feature with custom navigation back button item.
Current Implementation:
After researching a lot, I found the following solution to be best:
Set the delegate of the gesture recognizer as follows
self.navigationController.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
This, creates a lot of bugs as mentioned in this stackoverflow answer. To avoid that, subclassing the UINavigationController seems to be the only feasible option. I did that as mentioned in this blog by Keighl.
Problem:
Basic swipe to back feature is working, but the strange thing is that, sometimes, the same viewController that is being dismissed, appears again after the pop action is completed.
i.e. suppose the navigation stack looks like A -> B. Popping B will again bring up B. This keeps on happening until eventually the viewController B actually gets dismissed and A appears.
This happens to all views in all viewController objects and not just to a specific one.
Also, I have ensured that the push method is called only once at all places.
I also tried logging the navigation stack at each point, but there is only one instance of each viewController.
Point to note:
I need to disable the swipe feature in certain views. I did this by writing the code to disable and enable the swipe gesture in viewDidAppear and viewDidDisappear respectively.
Please provide your valuable suggestions or a solution to this problem. Thanks!
Short answer: You should add a UIScreenEdgePanGestureRecognizer to your view controller if you want to add a pop gesture where none exists. Modifying the existing interactivePopGestureRecognizer is probably not the right approach. Do this:
[self addGestureRecognizer:({
UIScreenEdgePanGestureRecognizer *gesture =
[[UIScreenEdgePanGestureRecognizer alloc]
initWithTarget:self action:#selector(pop)];
gesture;
})];
and
-(void)pop {
// pop your view controller here
}
Long answer: Forcing the interactivePopGestureRecognizer.delegate is what breaks your code.
If you need to cast self as such:
self.navigationController.interactivePopGestureRecognizer.delegate =
(id<UIGestureRecognizerDelegate>)self;
...it is because self is not a UIGestureRecognizerDelegate. The following should compile, link, build and run or you are setting yourself up for trouble:
self.navigationController.interactivePopGestureRecognizer.delegate = self;
Note that being a UIGestureRecognizerDelegate specifically allows you to tweak a gesture's behavior at runtime, assuming you are implementing one of the following and ensuring that the tweak applies to a gesture you own:
gestureRecognizerShouldBegin:
gestureRecognizer:shouldReceiveTouch:
gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:
gestureRecognizer:shouldRequireFailureOfGestureRecognizer:
gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:
By constantly changing the delegate of that interactivePopGestureRecognizer you did not create, all you are doing is preventing the iOS behavior to take place.
From the documentation
UINavigationController -interactivePopGestureRecognizer
The gesture recognizer responsible for popping the top view controller off the navigation stack. (read-only)
In plain English: Use this value is you need to combine that gesture with your own gesture. But you are not supposed to modify its behavior:
...You can use this property to retrieve the gesture recognizer and tie it to the behavior of other gesture recognizers in your user interface...

3 programmatically created UIButtons - how to go between 3 UIViewControllers using those buttons with Storyboard?

Playing around with some Objective-C (well, iOS) and have done the following... loaded some UIButtons programmatically into a UIScrollView. That works well. Though I've always just connected UIViewControllers together using control-click and drag. Now I've created buttons programmatically, I have no idea how to go from one view controller to another in a Storyboard because there is nothing to drag from!
I'm not really sure what code to post as such, because I haven't done anything that /nearly/ works or doesn't work as such. I get how to do it with XIBs. But I suppose the question is : 3 UIButtons have been created programmatically and I have 3 UIViewControllers. How do I access those ViewControllers using my UIButtons?
Thanks
In the Interface builder view control click and drag from the viewcontroller icon under the first view controller, to the middle of the second view controller. A segue will be created, selected the appropriate type.
Now select the segue and in the inspector give it a unique identifier (say 'myNewSegue').
Now in your first viewcontroller you can create a method that has the following code:
-(void)myButtonAction:(id)sender {
[self performSegueWithIdentifier:#"myNewSegue" sender:self];
}
And add this method as a target action to your button:
[myButton addTarget:self
action:#selector(myButtonAction:)
forControlEvents:UIControlEventTouchUpInside];]
A segue doesn't have to have a button at the leading end of it; you can instead draw it from an entire view controller to another. You can also give a segue an identifier, a string that's used as a name for that segue. Once you've done that, you can programmatically trigger that segue by calling -performSegueWithIdentifier:sender:.
To actually call -performSegueWithIdentifier:sender:, though, you'll need to connect the button to a target and action. If you've never done that, read the Event Handling Guide for iOS in your documentation.

UIBarButtonItem sent action works on iOS 6 but not iOS 5

I am going to tear my hair out because I cannot think of a logical reason why this occurs on 5 but not 6. So basically, I have a view presented in modal fashion with a navigation bar and a Cancel button (UIBarButtonItem - no custom anything on it, just a standard button) in the navigation bar. When this Cancel button is tapped, I want the modal view to disappear. This works just fine in iOS 6. But for iOS 5 it refuses to work for 2 out of 3 places I have it in my code. What's odd to me is that it works on one but not the other two.
Using storyboards, I right click the button, click on "selector" under Sent Actions, and drag over to the appropriate IBAction method in my view controller's .h file. The link is successfully confirmed with the little blinking animation in Xcode. When I run the app on iOS 6 (simulator or device doesn't matter), the method is successfully executed upon button tap and my modal view dismissed. However, on iOS 5, the method is never even called (I set breakpoints inside the method to see if they would be hit). I've even tried switching the argument in my IBAction method from id to UIBarButtonItem *. No cigar, though.
I've also tried programmatically (in viewDidLoad) setting the cancel button's action to a selector. I've even set the target to the VC. No cigar again. Here is a screenshots of my current setup:
Please note the IBAction methods.
Is there some magical clause in the documentation that I missed? Something awfully special I need to do in order to get it to work in iOS 5? It sure seems like a bug to me, but I'm still fairly new to this stuff so what do I know.
I am using Xcode 4.5.2 and storyboards, and targeting iOS 5 and iOS 6 for the release.
Your help is appreciated, thank you.
I think there may be problem of using GestureRecognizer please comment that code and try it...
I added a separate UIView that resides under my textfields and button and below the navigation bar/title. I added the tap gesture to that programmatically, and that seems to recognize both my tap and the cancel button's action. I still would like to know why the tap gesture swallows up the UIBarButtonItem's action if the gesture is on the root view. This question helped me figure this out. Thanks.
you can exclude view/control from gesture recognizer using following delegate method.
// UIGestureRecognizerDelegate
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
// exclude your view from gesture recognizer
if(yourView == touch.view)
{
return NO;
}
//
return YES;
}

Interact with other views while a popover is active

I have a toolBar and I have setup two UIBarButtonItem on it. Both UIBarButtonItem are containing UIButtons as their customViews.
I activate a popover for their Touch Up Inside event as below,
[popover1 presentPopoverFromBarButtonItem:buttonItem1 permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
I have another UIButton named clearFilters inside the main view. (Also this is the view which is containing the above toolBar.) I have declared a method for clearFilters button's Touch Up Inside event.
My problem is,
I can not interact with the clearFilters button while a popover is active. So, I'm looking for a solution to interact with this clearFilters button, while a popover is active.
I tried by adding passthroughViews property for a popover as below and it do not work as I expect.
popover1.passthroughViews = [NSArray arrayWithObject:clearFiltersButton];
What could be the reason. As the documentation has mentioned I can not see any issue.
I expect if the above things are correct, then the Touch Up Inside event of the the clearFilters button's should be fire up.
So, please show me if there is any issue or a necessary way to work on this thing.
I'm working on XCode4 and iOS 4.3.
Thanks.
The UIPopoverController documentation reveals why the other bar buttons can be tapped while the popover is visible:
“When presenting the popover, this method adds the toolbar that owns the button to the popover’s list of passthrough views.”
Try querying and logging the popover’s passthrough views. Does it already have things in it? Perhaps something like this would work?
myPopover.passthroughViews = [myPopover.passthroughViews arrayByAddingObject:clearFilters];
I haven’t tested this code, but it’s worth a try.

UIScrollView on UIWebView and UITapGestureRecognizer conflicts

I have a UITapGestureRecognizer on a UIViewController, which has a UIScrollView and UIWebView inside. It recognizes the tap gesture only after I scroll the UIWebView. How could I prevent this ?. Basically I want the tap gesture to be detected, when I am not scrolling the web view. I looked around and the closest I found is this:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer*)otherGestureRecognizer {
return YES;
}
but not sure how can I use this to disable the tap while scrolling. Any idea?
Another thing I want to do is to disable the UITapGestureRecognizer, when a link on the UIWebView is clicked (shouldStartLoadWebRequest is called). I checked that the tap gesture recognizer is called, before the shouldStartLoadWebRequest is called. Basically when clicking on a link on a UIWebView, it shouldn't trigger the action invoked by the UITapGestureRecongnizer. Any idea on how to do this?
So Apple's documentation strongly recommends you don't nest a UIWebView inside a scroll view:
Important: You should not embed UIWebView or UITableView objects in UIScrollView objects. If you do so, unexpected behavior can result because touch events for the two objects can be mixed up and wrongly handled.
It is possible on iOS 5 and above to get direct access to the underlying scroll view on a UIWebView (using the scrollView) property - playing around with this might help you.