This question already has answers here:
UIScrollView touchesBegan
(8 answers)
Closed 9 years ago.
I have subclassed UIScrollView and implemented
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
method in it.
But Nothing happens when click or hold the scroll view!
Edit: I also need to use touchesEnded method
I think UIScrollView has two gesture recognizer. They are responsible for handling touch sequences, so they swallow the touch events.
Use scrollView delegate methods to handle drag gestures or the
touchesShouldBegin:withEvent:inContentView:
method to handle scrollview content touches and
touchesShouldCancelInContentView:
to cancel it.
As alternative you can manipulate the panGesture recognizer of the scrollView to pass the event to the next responser.
In your subclass of UIScrollView override the hitTest method like this:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
UIView *result = nil;
for (UIView *child in self.subviews)
if ([child pointInside:point withEvent:event])
if ((result = [child hitTest:point withEvent:event]) != nil)
break;
return result;
}
I think you are using scrollview as a subview.So in that case you can use gesture coz same problem i've face it.
You can do using UITapGestureRecognizer like this ...
-(void)viewDidLoad
{
UITapGestureRecognizer *gr = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
gr.delegate = self;
[self.scrollView addGestureRecognizer:gr];
}
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
// do here whatever you wanna ..........
}
Solved!
Used LongPressedGestureRecognizer. The action method keeps getting called till the user holds the view. From that i can figure out the state of the gestureRecognizer (UIGestureRecognizerStateBegan, ...Ended, ...Cancelled)
Related
I have a 3 views in one screen with different components such as tables and buttons, when I try to dismiss the keyboard with the code below it doesn't dismiss when I click on the view that contains the textView( and that’s what I need! ) . However when I click on one of the other views it dismisses.
The following image shows the views that I have and the last view inside the scrollview is the one that contains the textView.
How can I get the keyboard to dismiss despite the view that is taped.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch * touch = [touches anyObject];
if(touch.phase == UITouchPhaseBegan) {
[aTextField resignFirstResponder];
}
}
also this code fails to dismiss:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[[self view] endEditing:TRUE];
}
You need to add tap gesture in the views which you want to click:
For you are using two UIViews with name Your_view1 and Your_view2, and textfields are txt1 and txt2. The code is below:
Your_view1:
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapGesture:)];
singleTapGesture.numberOfTapsRequired = 1;
[Your_view1 addGestureRecognizer:singleTapGesture];
Your_view2:
UITapGestureRecognizer *singleTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleSingleTapGesture:)];
singleTapGesture.numberOfTapsRequired = 1;
[Your_view2 addGestureRecognizer:singleTapGesture];
And the delegate is:
-(void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTapGesture{
[txt1 resignFirstResponder];
[txt2 resignFirstResponder];
}
If your textView is editable , tapping on that, will make it first responder. That’s why keyboard is not dismissing. If you don’t need edit textView, set it’s editable attribute to false
You should take a look at Apple document for UIScrollView.
Because a scroll view has no scroll bars, it must know whether a touch signals an intent to scroll versus an intent to track a subview in the content. To make this determination, it temporarily intercepts a touch-down event by starting a timer and, before the timer fires, seeing if the touching finger makes any movement.
That's why touchesBegan doesn't work when you click inside your scroll view.
A easy way to fix it, add a UITapGestureRecognizer for your viewController.view.
#property (nonatomic, strong) UITapGestureRecognizer *tapGestureRecognizer;
- (void)viewDidLoad {
[super viewDidLoad];
self.tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self.aTextField action:#selector(resignFirstResponder)];
[self.view addGestureRecognizer:self.tapGestureRecognizer];
}
managing keyboard get most of our development time luckly we have an alternative solution with this library
No need to write multiple line of code in every viewcontroller simply add one line of code in your didFinishLaunchingWithOptions
IQKeyboardManager.sharedManager().enable = true
This will save time
Hope this will help you
I am building a iOS app. I have a UIWebView that is added as a subview to self.view, then another view, which is mapView, added as a subview of the web view. But the mapView is send to the back of the webView. The background of the webView is transparent so that one can see the map.
see the code:
[self.webView addSubview: self.mapView];
[self.webView sendSubviewToBack: self.mapView];
Well what I am trying to do is to pass the gestures of the webView to the mapView so that the user can drag the map.
I have marked the cancelsTouchesInView property to NO for both the webView and the mapView.
I have added a gesture recognizer for the webView. The selector does get called. But what am I supposed to do next?
self.webPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action: #selector(handleWebPanGesture:)];
[self.webView addGestureRecognizer: self.webPanGesture];
I called the gestureRecognizerShouldBegin method in the webView selector, but it doesn't work.
- ( void ) handleWebPanGesture: ( UIPanGestureRecognizer *)gesture
{
NSLog(#"WebPanGesture recognizer called!");
[self.mapView gestureRecognizerShouldBegin: gesture];
[self panAction: gesture];
self.mapPanGesture = gesture; // the mapPanGesture property is the Gesture recognizer for the map
}
I also call this function
- ( IBAction )panAction:(UIPanGestureRecognizer *)sender {
NSLog(#"panAction called!");
CGPoint move = [sender translationInView:self.webView];
CGPoint newCenter = subViewCenter;
newCenter.x += move.x; newCenter.y += move.y;
self.myMapView.mapView.center = newCenter;
}
but it doesn't make the map draggable, it just moves it.
self.mapPanGesture = gesture //doesn't work as well.
How can I target the actions to the mapView so that the map gets dragged when drag on the webView?
I sure you should use overlays (MKOverlay) on mapView to show content on map. Because this is much easier way to achieve what you need.
Please read this Adding Annotations to a Map
Here I found a way around so do check out this link might be helpful.
In short, webView doesn't handle touchBegan method so u need to subclass that and in touch began method u could pass the following,
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"%#",event);
[super touchesBegan:touches withEvent:event];
[_mapView touchesBegan:touches withEvent:event];
}
or check out for below method,
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
// If the hitView is THIS view, return the view that you want to receive the touch instead:
if (hitView == self) {
return otherView;
}
// Else return the hitView (as it could be one of this view's buttons):
return hitView;
}
Above hitTest is with reference to this link.
Hope, this much info is useful to u.
I googled several other questions and tutorials but couldn't find an answer for my question. I want to detect if the user has touched/tapped/hold/clicked the screen. I tried with touchesBegan: withEvent: but it is not firing any events.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if ([touch view] == mapView_) {
NSLog(#"Touches began");
} else NSLog(#"Touches began");
}
Is there another way to detect user interaction throught touching the screen?
You have to use UITapGestureRecognizer.
Conform your class to UIGestureRecognizerDelegate protocol.
Instantiate the gesture recognizer. For example, to instantiate a UITapGestureRecognizer, we will do:
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapFrom:)];
Here, action is the selector which will handle the gesture. Here, our selector handleTapFrom will look something like:
- (void) handleTapFrom: (UITapGestureRecognizer *)recognizer
{
//Code to handle the gesture
}
The argument to the selector is the gesture recognizer. We can use this gesture recognizer to access its properties, for example, we can find the state of the gesture recognizer, like, UIGestureRecognizerStateBegan, UIGestureRecognizerStateEnded, etc.
Set the desired properties on the instantiated gesture recognizer. For example, for a UITapGestureRecognizer, we can set the properties numberOfTapsRequired, and numberOfTouchesRequired.
Add the gesture recognizer to the view you want to detect gestures for. In our sample code (I will be sharing that code for your reference), we will add gesture recognizers to an imageView with the following line of code:
[self.imageView addGestureRecognizer:tapGestureRecognizer];
After adding the gesture recognizer to the view, set the delegate for the gesture recognizer, i.e. the class which will handle all the gesture recognizer stuff. In our sample code, it would be like:
tapGestureRecognizer.delegate = self;
Note: Assign the delegate after adding the gesture recognizer to the view. Otherwise, the action method won’t be called.
Reference - Here
The solution was using UIPanGestureRecogniser.
Here's the code that solved the problems and stopped my headache:
UIPanGestureRecognizer *tap = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapFrom:)];
[mapView_ setMultipleTouchEnabled:YES];
[mapView_ setUserInteractionEnabled:YES];
mapView_.gestureRecognizers = #[tap];
And then the selector method:
- (void) handleTapFrom: (UIPanGestureRecognizer*)recogniser {
NSLog(#"Pin");
}
You can subclass UIApplication if you are only interested in whether the user touched the screen or not, and implement this method:
- (void)sendEvent:(UIEvent *)event {
[super sendEvent:event];
if (event.type == UIEventTypeTouches) {
// handling code
}
}
In this case the main.m file would look like this:
int main(int argc, char * argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, #"UIApplicationSubclass", #"AppDelegate");
}
}
I am trying to create a custom UIControl similar to a slider.
This control is to be the subview of a view that also has a tap gesture recognizer attached to it.
The problem now is that this tap gesture recognizer cancels the touches sent to my control. Is there a way I can override this from within the code of my control?
If I look into the standard controls in iOS it looks as if UIButton has a way of overriding the tap gesture recognizer but UISlider doesn't. So if I replace my custom control with a UIButton the tap gesture recognizer does not trigger its action, but if I replace it with a slider it does.
edit: I made a small project in Xcode to play around in. Download here https://dl.dropboxusercontent.com/u/165243/TouchConcept.zip and try to change it so that
The UICustomControl does not know about the tap gesture recognizer
The UICustomControl is not cancelled when the user taps down on the yellow box
The UICustomControl does not inherit from UIButton (that is a solution that does not feel right and might give me more headaches later on)
The code:
// inherit from UIButton will give the wanted behavior, inherit from UIView (or UIControl) gives
// touchesCancelled by the gesture recognizer
#interface UICustomControl : UIView
#end
#implementation UICustomControl
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesBegan"); }
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesMoved"); }
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesEnded"); }
-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{ NSLog(#"touchesCancelled"); }
#end
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(logTap:)];
[self.view addGestureRecognizer:tapRecognizer];
UIView *interceptingView = [[UICustomControl alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
interceptingView.userInteractionEnabled = YES;
interceptingView.backgroundColor = [UIColor yellowColor];
[self.view addSubview: interceptingView];
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void) logTap: (id) sender
{
NSLog(#"gesture recognizer fired");
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
You can configure the gesture recognizer to not cancel touches in the view it's attached using the "cancels touches in view" property:
myGestureRecognizer.cancelsTouchesInView = NO;
I'm a little bit late, but for those (like me) stumbling into this question, I used an alternative solution:
Use the delegate of the gesture recogniser.
UITapGestureRecognizer *tapGestRec = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(dismissInfoBox:)];
tapGestRec.delegate = self;
[self.view addGestureRecognizer:tapGestRec];
Then do a sort of hit test in the shouldReceiveTouch delegate function when the gesture recogniser wants to handle/swallow a touch.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
CGPoint location = [touch locationInView:self.view];
return !CGRectContainsPoint(self.myCustomControl.frame, location) && !CGRectContainsPoint(self.myOtherCustomControl.frame, location);
}
I did all this in my ViewController, this way the UIControl does not have to know about any sibling views and the gesture recogniser does not 'steal' taps from my custom controls and only handles 'uncaught' taps.
Also, this way you won't trigger both the gesture recogniser and the custom control, which would happen with cancelsTouchesInView.
BTW, maybe it works with UIButton because UIButton uses gesture recognisers internally? I think they understand each other, while UIControls and recognisers do not. Not sure though.
override gestureRecognizerShouldBegin(_:) in your UIControl subclass.
public override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer.isKind(of: UITapGestureRecognizer.self) {
return false
} else {
return super.gestureRecognizerShouldBegin(gestureRecognizer)
}
}
You should follow this tutorial
http://www.raywenderlich.com/1768/uiview-tutorial-for-ios-how-to-make-a-custom-uiview-in-ios-5-a-5-star-rating-view
It shows you How to make a custom uiview.
And then do follow this one
http://www.raywenderlich.com/29474/ipad-for-iphone-developers-101-in-ios-6-custom-input-view-tutorial
I've created a mini pop-up menu for the iPhone in a UIView, and I'd like the user to be able to dismiss the view if they do anything other than select one of the options. So, if a user taps/swipes/pinches any other element on the screen, the pop-up view should disappear.
However, I don't want to detect a gesture that will stop something else from happening... For example, there is a UITableView underneath and if I swipe up or down on it, I want it to move as expected as well as dismissing the mini pop-up view.
Should I use multiple gesture recognizers, or should I use touchesBegan, or is there a better way of doing it?
Put this in your UIViewController
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
if (touch.view!=yourView && yourView) {
[yourView removeFromSuperview];
yourView=nil;
}
}
EDIT: changes made to detect touch and remove only if view exists
EDIT2: Well you could add the following to your UIButtons/UITableView methods
if (yourView) {
[yourView removeFromSuperview];
yourView=nil;
}
or add touchesBegan:withEvent: as a touchDown event to your buttons.
Both annoying to do but can't see another way to do it as the touchesBegan method doesn't get called with interactive elements.
EDIT3: Right scrap that, think i've nailed it
in your interface add the UIGestureRecognizerDelegate
#interface ViewController : UIViewController <UIGestureRecognizerDelegate> {
then in your viewDidLoad add this
UITapGestureRecognizer *tapped = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapMethod)];
tapped.delegate=self;
tapped.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tapped];
then in your viewController add these 2 methods
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (touch.view!=yourView && yourView) {
return YES;
}
return NO;
}
-(void)tapMethod {
[yourView removeFromSuperview];
yourView=nil;
}