Detecting a click inside a UIWebView - not on a link - ios

I want to enable some functionality that is triggered by clicking on the screen. the view of the screen is a UIWebView, and I'd like to be able to tell when the user clicks on it, though not when he clicks on a link inside of it, but any other place.
Any Ideas?
Tnx!

Have you looked at touchesBegan: / touchesEnded: in the UIResponder Class Reference

look at this code snippet. it might help you.
NSSet *touch;
CGPoint mTouchLocation;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
touch = [event allTouches];
mTouchLocation = [[touches anyObject] locationInView:self.view];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesMoving");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchEnded");
CGPoint newTouchLocation = [[touches anyObject] locationInView:self.view];
}
touchesBegan is invoked when user touched the screen, touchesMoved when the user move the finger holding the screen and touchesEnded is when the touch is released.

Related

add gesturerecognizer on top right side of navigation bar

I use the following code to add a gesture recognizer on the top right side of uinavigationbar but i get result if i tap anywhere on the navbar. How am i supposed to make a gesture for the top right corner?
- (void)handleGestureForTopRightBarButtonItem:(UIGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.navigationController.navigationBar];
if (CGRectContainsPoint(CGRectMake(self.navigationController.navigationController.view.width-20,0,100,self.navigationController.navigationController.view.height), p)) {
NSLog(#"got a tap on the right side in the region i care about");
} else {
NSLog(#"got a tap on the right side, but not where i need it");
}
}
As UIGestureRecognizer is reporting to a class object there are a couple of ways to solve this. UIGestureRecognizer was not meant to be stacked multiple times on the same view, if you do so you would very likely drain more Energy than you need apart from the loss of CPU power and lots of comparison code that has to distinguish all the running recognisers. But it would work..
a) write code that compares its coordinates and expected values and if they match in the range you want do your actions.
b) create another object that is living only in the coordinates you want and has it own UIGestureRecognizer. Not ideal, as written above.
c) use the power of UIControl which are also inherited UIView's that are also UIResponders.
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(nullable UIEvent *)event;
- (void)endTrackingWithTouch:(nullable UITouch *)touch withEvent:(nullable UIEvent *)event; // touch is sometimes nil if cancelTracking calls through to this.
- (void)cancelTrackingWithEvent:(nullable UIEvent *)event;
d) use the power of UIView without a UIGestureRecognizer. Which by the way basically works also on CALayers, they do not have the sendAction methods, they are not UIControls.
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
BOOL inside = [super pointInside:point withEvent:event];
if (inside && event.type == UIEventTypeTouches) {
//[super sendActionsForControlEvents:UIControlEventTouchDown];
}
return inside;
}
e) code some Class that inherits from UIResponder, which basically is what UIControls do and use their API instead so you make use of touch coordinates as well.
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
[self someCoRoutineWithTouch:touch];
}
//[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved :(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
//do some stuff per touch
}
//[super touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (UITouch *touch in touches) {
//do some stuff per touch
}
//[super touchesEnded:touches withEvent:event];
}
-(void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
Just don't forget that UIGestureRecognizer is basically for gestures that are made of touches, even multiple touches but not mainly to catch touches in general, despite in a lot of examples they are used instead of coding a proper UIControl. Also dont forget in a Devices edges the recognition of gestures is limited by the nature of finger size.

Prevent parent view controller from receiving UITouch in child controller

So, in my app, then a map annotation is pressed, It reveals a modal view controller. Then when an element that modal controller is pressed, I open another, smaller controller. In both controllers, I have implemented the method
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
if (!CGRectContainsPoint([self.view viewWithTag:21].frame, touchPoint)) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
Here is an image that shows my layout:
My problem is that when I touch in yellow circle one, the second modal dismisses, and after that, the first modal dismisses. SO, how can I prevent the first modal from receiving the touch.
PS: both modals are transparent UIViews, with smaller views inside them to display the content.
Obviously the event is passed through the view controller to the next responder.
In the class reference of UIResponder.
The default implementation of this method does nothing. However
immediate UIKit subclasses of UIResponder, particularly UIView,
forward the message up the responder chain. To forward the message to
the next responder, send the message to super (the superclass
implementation); do not send the message directly to the next
responder. For example,
[super touchesMoved:touches withEvent:event];
If you override this method without calling super (a common use
pattern), you must also override the other methods for handling touch
events, if only as stub (empty) implementations.
So if you don't want the event to forward to the next responder, you should override the other methods.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint touchPoint = [touch locationInView:self.view];
if (!CGRectContainsPoint([self.view viewWithTag:21].frame, touchPoint)) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}

How to send message to UIViewController

I have a problem as following, I've developed a ios app, to be concice , It has a UIViewController as parent, also it has a button , and the UIViewController popup a transparent UIView as a mask. When I click on the UIView(exactly within the underlying button boundary ) , the button could not receive any event(such as "touch up inside"), how could the button get the "touch up inside" event from transparent the UIView which is above the UIViewController?
This is not possible directly as the event triggered will not be for the button as button is not visible
(ie. another View is completly covering the button and is blocking the interaction with the user).
But i can give u a work around.
1.Declare a Bool Variable in your UIViewController
2.Implement the touches methods as shown below
- (void)touchesBegin:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self.view];
if(CGRectContainsPoint(button.frame, p) && !boolVariable) {
boolVariable = YES;
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch=[touches anyObject];
CGPoint p=[touch locationInView:self.view];
// If the below condition is true then it mean there the user tapped on the same location as that of button..(touchesEnded and touchesCanceled was not called) so the event is just like touchUpInside
if(CGRectContainsPoint(button.frame, p) && boolVariable) {
boolVariable = NO;
[Here you can call the method which you wanted to call on touchUpInside of the button];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
boolVariable = NO;
}
- (void)touchesCanceled:(NSSet *)touches withEvent:(UIEvent *)event {
boolVariable = NO;
}
I was not able to test the able code on Xcode but i think it will work..
Note: The frame of the button should be with respect to the UIViewController.
Hope this helps u out :)
UIView instances, do not listen for touches. In order to get callbacks for events such as "touch up inside" are only sent to subclasses of UIControl
The most basic concrete subclass is UIButton.
Without code or more details about your app's setup, it's difficult to give better advice.

iOS action - how do i get event data from UIAction for a standard UIButton?

For UIButton, Is there a way on touchDragInside to tell what direction the gesture was in? (up, down, left, right)
- (IBAction)buttonTouchedUpInside:(UIButton *)sender forEvent:(UIEvent *)event {
}
Thanks!
You should to subclass the button and override beginTrackingWithTouch:withEvent:, continueTrackingWithTouch:withEvent:, and endTrackingWithTouch:withEvent:. The logs I put in the code below should get you started with what you want to do.
- (BOOL)beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
NSLog(#"%#",NSStringFromCGPoint([[event.allTouches anyObject] locationInView:self]));
return YES;
}
- (BOOL)continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
NSLog(#"%#",NSStringFromCGPoint([[event.allTouches anyObject] locationInView:self]));
return YES;
}
-(void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event {
NSLog(#"%#",NSStringFromCGPoint([[event.allTouches anyObject] locationInView:self]));
}
If you're only interested in the overall direction, you can just look at the beginTracking number and the endTracking number, and forget about the continueTracking method.

Order of multiple touches in UIGestureRecognizer

It seems that there is no guarantee about the order in which UITouches appear when looping through a UIGestureRecognizer's method [locationOfTouch: inView:]. Specifically:
for (int i = 0; i < [recognizer numberOfTouches]; i++) {
CGPoint point = [recognizer locationOfTouch:i inView:self];
NSLog(#"[%d]: (%.1f, %.1f)", i, point.x, point.y);
}
One would think that point with index 0 would be the first UITouch, or the first that was released, but quite often the order of 2 touches is mixed up. Does anyone know how to test for the order of those events? Unfortunately there is no access to the UITouch objects themselves (with the timestamp).
Also, no guarantee is made in the documentation that the touches from -locationOfTouch:inView: will always be in a reliable order. Can anyone confirm or deny this?
You could try setting the recognizer's delegate property and implementing -gestureRecognizer:shouldReceiveTouch:. It should be called sequentially, and you can then hold on to the touches.
It seems like to me like want to track i.e. two fingers of two hands independently, instead of recognizing a concrete gesture which is the goal of a UIGestureRecognizer, as the name says. If that's what you want i'd rather implement the UIResponder methods:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self touchesEnded:touches withEvent:event];
}
With this approach you really get a set of UITouch objects und can do advanced tracking.
Why not iterating through UITouches sorted by timestamp?

Resources