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;
}
Related
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...
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.
I have this UIPopOverScreen that shows when I press a NavigationBar Button.
The problem I want to dismiss it whenever I touch anything outside that popoverscreen.
It partly works but it doesn't dismiss when touching the navigationbar which has been built into the VC that calls the popoverscreen.
I've tried several options to make it disappear but it just hasn't worked yet.
The best results I've gotten when I set:
self.popOverController.passthroughViews = nil;
It worked as I wanted but by just setting this you have to press the navigationbar twice to make it respond again.
I would love to hear your thoughts about a clean solution.
How about adding a UITapGestureRecognizer to react to a tap on the navigation bar? Save a reference to the active Popover and dismiss it by calling a method when the navigation bar receives a tap.
I have a UIButton linked up in IB correctly(I believe). The button fires inconsistently, every time I reload the view to show updated info, the button works sometimes and sometimes does not.It gives no errors. I can't find a pattern to when it works and when it doesn't, the same code is run every time I open the view and it still works when it wants. Besides linking it in IB I have also tried to addTarget in ViewDidLoad and remove the IB connection but it still has the same inconsistency,
[_buttonScreen addTarget:self action:#selector(buttonScreenClicked) forControlEvents:UIControlEventTouchUpInside];
If I add NSLog(#"Clicked"); to buttonScreenClicked I see that the method doesn't always get called, what would cause it to do this, I have made sure that I set:
[_buttonScreen setAlpha:0.1];
[_buttonScreen setHidden:NO];
[_buttonScreen setUserInteractionEnabled:YES];
I have no Image, text, or color in the button, but it still works sometimes.
I'm using AFKPageFlipper on the same view but it still had the same problem before I added AFKPageFlipper, so I don't think its that.
If anyone could point me in any direction to start trouble shooting this problem I would appreciate.
Thanks
I just had the same problem and worked it out. The 5 seconds is the clue.
Somewhere you have a gesture recognizer covering the same space as your button. More specifically you have a gesture recognizer that is eating your Taps but not your LongPresses. If you just tap the button the gesture recognizer runs off with your event; Hold your finger down long enough and the gesture recognizer no longer considers it a tap and the event is passed through to your button.
Instrument your Tap gesture recognizer handlers and the problem should pop out at you.
Make sure you don't have any other UIView descendants overlaying the button (like a transparent UIScrollView) as these will intercept the touch events first.
Also make sure that the containing view (the view with the button in) is correctly sized, by default you can place a view outside the bounds of another view and the clipsToBounds is set to false so you will see it but not be able to interact with it.
Things to try:
Do you have any other actions on the button?
Do you have any other UIViews which could possibly be accepting the key presses (above or below, or un-shown)
Also, please check that you have only one UIViewController instance for this screen. Other issues may arrise because of that.
What happens if you dont set the alpha level?
Do you release the object properly in the dealloc only ?
Hope this helps
In my iPad app, I save the state (visible/not visible) of a popover. So, during the launch of the app I initialize the UIPopoverController and tell it to show itself by using presentPopoverFromBarButtonItem:permittedArrowDirections:animated:. For the first argument (UIBarButtonItem), I use self.navigationItem.rightBarButtonItem. However, the popover keeps showing up on the left side of the screen (and not underneath the targeted button).
After the app is launched, the behavior is as expected. Any suggestions how to solve this?
For your information, I initialize the rightBarButtonItem and assign it to the navigationItem in the viewDidLoad method and before asking the popover to present itself. I have tried to call the popover in viewWillAppear and viewDidLoad, but the effect is the same.
My best alternative is to use presentPopoverFromRect:inView:permittedArrowDirections:animated: instead and "guess" the position depending on the orientation of the device.
Update: when I rotate the iPad, the popover does jump to the correct position. It seems that the barButtonItem's position is only determined at the last minute and after I ask my popover to present itself.
In situations like these where timing appears to be important I found that the trick of postponing an action until the next iteration of the run loop helps. If developing for iOS 4.0+, this can be easily achieved with GDC:
// call from viewDidAppear:
dispatch_async(dispatch_get_main_queue(), ^{
// call presentPopoverFromBarButtonItem:permittedArrowDirections:animated: here
});