I have a UIView with a UINavigationBar, a UITabBar and a UITableView.
When i press the status bar the UITableView scrolls to top because i have it set to TRUE.
I want to be able to do the same, by pressing the UINavigationBar like it happens in some apps. Setting the UITableView to scrollsToTop = TRUE only works if the user presses the StatusBar.
Method 1:
How about adding a TapGestureRecogniser on your UINavigationBar? This will only work if you dont have any buttons on your navigationBar.
//Create a tap gesture with the method to call when tap gesture has been detected
UITapGestureRecognizer* tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(navBarClicked):];
//isolate tap to only the navigation bar
[self.navigationController.navigationBar addGestureRecognizer:tapRecognizer];
//same method name used when setting the tapGesure's selector
-(void)navBarClicked:(UIGestureRecognizer*)recognizer{
//add code to scroll your tableView to the top.
}
and that's about it really.
Some people have found that their back button stops working when adding a tap gesture to the navigation bar, so you can do one of two things:
Method 2: Set the user interaction enabled to yes and set the tap gesture recogniser like shown in method 2 in detail.
Method 3: Use a UIGestureRecognizerDelegate method called gestureRecognizer:shouldReceiveTouch and make it return NO when the touch's view is a button, otherwise return YES. See method 3 in detail.
Method 2 from point 1: - feels dirty/hackish
[[self.navigationController.navigationBar.subviews objectAtIndex:1] setUserInteractionEnabled:YES];
[[self.navigationController.navigationBar.subviews objectAtIndex:1] addGestureRecognizer:tapRecognizer];
Method 3 from point 2: - a lot better, the right way
implement the UIGestureRecognizerDelegate protocol in your .h file, and in your .m file add the following:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Disallow recognition of tap gestures when a navigation Item is tapped
if ((touch.view == backbutton)) {//your back button/left button/whatever buttons you have
return NO;
}
return YES;
}
Related
I have a small UITableView that is hidden when the view is loaded. When i click on "SHOW" UIButton, the UITableView is made visible by myTableView.hidden=NO;
I want to hide UITableView when a user touches outside its frame. Thanks for any help!
Best Approach
Simple.Before show up the UITable View add one more grayed out/Transparent view then add tap gesture recogniser on it to hide it . That's it.
Show Overlay View first - alpha will be 0.5f and background color should be clear color.
show the table view.
NOTE: over lay view should have tap recogniser which will hide the overlay and table view
in View did load
UITapGestureRecognizer *tapRecog = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(overlayDidTap:)];
[myOverLayView addGestureRecognizer:tapRecog];
- (void)overlayDidTap:(UITapGestureRecognizer *)gesture
{
//hide both overlay and table view here
}
Bad Approach
We should not add tap recogniser on main view itself. Because it may have lots of
controls inside of it. so when user tap on it. it will perform its operation. So to avoid
it we can simulate the same behaviour by above approach
You can get touch position by this:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(singleTapGestureCaptured:)];
[self.view addGestureRecognizer:singleTap];
- (void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture
{
CGPoint touchPoint=[gesture locationInView:self.View];
}
Then just check if the point is in tableview frame. If not then hide the tableview. Hope this help. :)
Subclass UITableView, override pointInside:withEvent:. It is templated for this reason.
Something like:
-(BOOL)pointInside:(CGPoint) point withEvent:(UIEvent*) event
{
BOOL pointIsInsideTableView = [super pointInside:point withEvent:event];
BOOL pointIsOutsideTableView = // Some test
return pointIsInsideTableView || pointIsOutsideTableView;
}
So you can catch the touch in table view implementation, where it belongs logically in this case.
I have created a customViewController to act as a customActionsheet. In this customViewController I have a UIView as the main view (self.view) and an IBOutlet UIView that is the custom action sheet (actionSheetView). What I am trying to do is make this custom action sheet act like a regular actionsheet where if you tap in the dark area, in my case the view with blackColor background and alpha 5.0. This is what I have:
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(slideOut)];
tapGesture.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapGesture];
The problem with this is that the subview, the custom action sheet view, also gets the tapgesture so tapping anything on the actionSheetView will get the tap gesture. I have tried a few things like - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch and self.actionSheetView.userInteractionEnabled = NO; but both do not seem to work. Anyone have any idea of how I can make the self.view tappable but disable that gesture for self.actionSheetView? Any tips, guidance, or help is greatly appreciated. Thanks in advance.
I was working on this same style of system. I found it works if you:
Add a new UIView as a subview of the main UIView.
Create another UIView where you want the custom UI (could be a table like an action sheet, a picker, etc). Make sure this shows above the UIView in step 1.
Register the UITapGestureRecognizer on the UIView from step 1.
(Optional) Add extra buttons (e.g. "Cancel" and "Done") and set up a delegate that your custom controller fires.
The main UIView (self.view) should be opague, clear color with alpha of 1. The full subview (see step 1) can be any color (I used light gray color), but adjust the alpha value (0.5 for me).
Unfortunately I can't post an image because I don't have enough rep on the site yet.
If you're using IB and having trouble receiving taps, check under the Attributes Inspector (under the view section) to make sure that "User Interaction" is enabled.
You need to set the delegate
tapGesture.delegate = self.actionSheetView
//actionSheetView.m
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
return NO;
}
For some reason my UITapGestureRecognizer is blocking my toolbar buttons from being pressed when the recognizer is added to self.view and I don't want it to. In shouldReceiveTouch I want to return NO if the item is the toolbar button.
How do I do this, however? The items aren't UIBarButtonItems apparently, because when I put an if statement to check if touch.view is of that class, it ignores it. If I put a breakpoint there and inspect touch.view its class is UIToolbarTextButton. But [UIToolbarTextButton class] I get a "use of undeclared identifier UIToolbarTextButton" error.
Can I say if it's a subview of UIToolBar? What should I do?
Without any code, this is a hard question to answer... However it sounds like you are adding the Tapgesture recognizer to the same view as your toolbar... You could check the coordinates to see if they are within the CGrect of the uitoolbar... but really what I would recommend is creating a view which contains 2 subviews: one is your toolbar, the other is the main part of your view... then add the tapGesture only to the mainView. Good luck
The better solution is not to add the UITapGestureRecognizer to the self.view.
Add a new view with Interface Builder that covers all the area but the toolbar area.
Put all your controls inside it and add the tap gesture to it.
Alternative: Add this to your ViewDidLoad function or somewhere you find more appropriate.
for (UIView *v in [toolbar items])
{
v.tag = 5; // tag tool bar items
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view.tag == 5)
return NO;
return YES;
}
I have a simple MapKit app working fine in iOS. It has annotation and when the user clicks on them, the little gray default popup is displayed with the title / subtitle. I even added a UIButton view into it.
So the problem is, I have a search bar above my map. I wanted to resignFirstResponder from the search box whenever the user clicks on the MapView, so I added a simple tap gesture responder. Worked great except now the little gray detail popups no longer show up (only the annotation pins)! I can still tap, zoom, move around etc. Just no popups.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
tap.cancelsTouchesInView = NO;
tap.delaysTouchesBegan = NO;
tap.delaysTouchesEnded = NO;
[mapView addGestureRecognizer:tap];
-(IBAction)tapped:(UITapGestureRecognizer *)geture {
[searchBar resignFirstResponder];
}
Is it possible to have the best of both worlds?
I used a delegate method similar to the following to arbitrate between touches that should go to my custom view's pan gesture recognizer and touches that should go to the scroll view that contained my custom view. Something like it might work for you.
// the following UIGestureRecognizerDelegate method returns YES by default.
// we modify it so that the tap gesture recognizer only returns YES if
// the search bar is first responder; otherwise it returns NO.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
if ((gestureRecognizer == self.tapGestureRecognizer) &&
(gestureRecognizer.view == self.mapView) &&
[searchBar isFirstResponder])
{
return YES; // return YES so that the tapGestureRecognizer can deal with the tap and resign first responder
}
else
{
return NO; // return NO so that the touch is sent up the responder chain for the map view to deal with it
}
}
I have set up some gesture recognition in an app that I'm building. One of the gestures is a single finger single tap, which hides the toolbar at the top of the screen. Works great except for one thing: a tap on a link causes the toolbar to go away, too.
Is it possible to detect a tap that was not a tap on a link? Could I do this by seeing where the tap occurred, and only take action if it didn't occur on an html link? Is this is possible, a pointer to how to determine if a link was tapped would be helpful.
Per Octys suggestion, I did attempt to wrap the UIWebView inside a UIView. I'm using interface builder, so I inserted a view in the hierarchy, and made the UIWebView a "child" of the new UIView. The hierarchy looks like this now:
I added an IBOutlet for the view in my view controller, and linked it up in interface builder.
I changed the gesture recognizer setup to look like this:
UITapGestureRecognizer* singleTap=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(handleSingleTap:)];
singleTap.numberOfTouchesRequired=1;
singleTap.delegate=self;
[self.UIWebViewContainer addGestureRecognizer:singleTap];
[singleTap release];
This code resides in viewDidLoad.
As before, the code correctly sees a single finger single tap, but a tap on a link in the UIWebView also causes the toolbar to go away. I only want the toolbar to go away if a link was NOT tapped.
Anyone have any suggestions?
Thank you.
Chris
Thanks
Chris
Try wrapping your UIWebView in a UIView container and set your gesture recognizers on the container view. Touch events that are not handled by the UIWebView will be passed up the view hierarchy and be intercepted by your container view, assuming it implements the appropriate handlers (and it is these handlers that should implement the code for hiding the toolbars...).
OK, so one "hack-ish" workaround is to make the view controller which houses the UIWebView a delegate of the gesture recognizer that you instantiate(UIGestureRecognizerDelegate) I am usually not a fan of solutions like this one, but...
Assign the gesture recognizer to the view that wraps you web view, and set it's delegate to self.
then in the delegate method gestureRecognizer:shouldRecieveTouch method, set the state to indicate the there was a tap event,
Return NO from the delegate method so the event propagates down the responder chain to the UIWebView.
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
_userTapped = YES;
[self performSelectorOnMainThread:#selector(hideMenuIfNotLink) afterDelay:??.?];
//??.?? amount of time to lapse to see if shouldStartLoadWithRequest gets called.
return NO;
}
-(void)hideMenuIfNotLink{
if(_userTapped)
//hideMenu
}
Now, in your UIWebViewDelegate shouldStartLoadWithRequest (which gets called when a user has in fact clicked a link)
if(_userTapped){
_userTapped = NO;
//possibly code to reshow or verify keep showing menu
}
You can use the UIWebViewDelegate protocol's -webView:​shouldStartLoadWithRequest:​navigationType: method.
If the navigationType is UIWebViewNavigationTypeLinkClicked, you can get the URL for the click by checking [request URL].
I've been looking for the same thing and found this: There is an iOS specific property that disables the callout when you hold your finger on a link. You add this into the styling (CSS) of your urls.
-webkit-touch-callout: none;
This worked for me.try adding the below line of code
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
I was having the same problem. This solution worked for me:
1) make sure to add the protocol to your interface: UIGestureRecognizerDelegate
for example:
#interface ViewController : UIViewController _SlideViewProtocol, UIGestureRecognizerDelegate_
2) add this line of code
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
}
3)
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(nextSlide)];
swipeGesture.numberOfTouchesRequired = 1;
swipeGesture.direction = (UISwipeGestureRecognizerDirectionLeft);
swipeGesture.cancelsTouchesInView = YES;
[swipeGesture setDelegate:self];
[self.view addGestureRecognizer:swipeGesture];