Pass method calls to object - ios

I am trying to create a custom swipe gesture recognizer to set allowed angles and distances.
So far it works really well but needs a lot of code in the view controller. So my question is wether it is possible to somehow pass a method call like:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
in the view controller to my gesture recogniser object so that I don't have to write something like this
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.swipeGestureRecognizer touchesBegan:touches withEvent:event];
}
into my view controller. I think the build in UIGestureRecognizer does this somehow but I could not figure out how it works.
I really would appreciate your help.
[EDIT]
Generic example: I have an object A which creates an object B. A has a method c that gets called sometimes. What I want to achieve is that a method d of B gets called when c in A gets called. So to somehow forward or pass this method call.
Specific example: The following method gets called in my view controller every time there is a touch on the screen:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
but I actually want my GestureRecognizer object to process this information so I have the same method in my GestureRecognizer object and I want this method to get called every time the counterpart in the view controller gets called.

Sub class UIGestureRecognizer and create a class called CustomGestureRecognizer.
Its .m file will look like this
#implementation CustomGestureRecognizer
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
// do your code here
}
#end
Some where in the subclass you have to change the state of the recognizer to
UIGestureRecognizerStateRecognized
when the current gesture is registered, else change to
UIGestureRecognizerStateFailed
.
Now in the view controller where you want to use this recognizer do :
CustomGestureRecognizer * recognizer = [[CustomGestureRecognizer alloc] initWithTarget:self action:#selector(handleChange:)];
recognizer.delegate = self;
[self.view addGestureRecognizer:recognizer];
Detailed explanation is given here Apple doc, under subclassing.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.swipeGestureRecognizer touchesBegan:touches withEvent:event];
}
The above will end up with Recursive calls, and your program will crash at some point of time.

Related

how to stop touchesEnded: withEvent: propogate from subView to ViewController?

I have a view being added as a subView of a viewController.
Both this subview and viewController all implement this method
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
I found that when I tapped the subView, both these method will be called.
I want to call subview's touchesEnded only. How to achieve this nicely? (not to add a gesture in it)
In the touchesEnded, apple doc says this "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."
what's the other method ?
You are close!
To prevent to pass touch event to superview, you should override all the methods for the touch events. Add all the touch event methods to your subview, then you should be OK.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
}
Instead of implementing the subview's touchesDidEnd why not do the whole work in the superview's touchesDidEnd something like this
if([touch anyObject].view == subview){
return;
}
This way you will be able to know whether the touch was originally from the subview or the superview.
Alternative : You can implement both the methods in the superview and subview, but like the above code you can return the call in the superview, if the view it interacted with is the subview, and keep working in the touches code in the subview
If in case you want a different opinion plz let us know what you are trying so that we can give the answer accordingly

Touches Ended not called when subclassed with UIGestureRecogniser

I am trying to create a custom gesture recogniser for my view. I am following this answer mentioned in here: But for some reason the touched Ended and also touches movied are not getting called. Only touches began get called.
SubClass:
#import "TapGesture.h"
#import <UIKit/UIGestureRecognizerSubclass.h>
#implementation TapGesture
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
if (self.state == UIGestureRecognizerStatePossible) {
self.state = UIGestureRecognizerStateRecognized;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
self.state = UIGestureRecognizerStateFailed;
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
self.state = UIGestureRecognizerStateFailed;
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
}
#end
and I am initialising the TapGesture as follows:
TapGesture *tapGesture=[[TapGesture alloc]initWithTarget:self action:#selector(incrementValue)];
tapGesture.delaysTouchesEnded=NO;
tapGesture.cancelsTouchesInView=NO;
[_workButton addGestureRecognizer:tapGesture]; //_workButton is a UIView
I don;t have any other gesture recognisers in the view. If I use the same methods in the UIView, all of them are called as expected.
Why the touchesEnded/touchesMoved are not getting called when overrride in UIGestureRecogniser class ?
When subclassing UIGestureRecognizer you must make it act like a continuous gesture and handle it's state machine by yourself (i.e., manually set it's state).
From the iOS Developer Library Docs on UIGestureRecognizer:
Subclasses must set the state property to the appropriate value when they transition between states.
See here for more info (Scroll down to Subclasing Notes)
Note: to make state read/write and not read-only, you should use UIGestureRecognizerSubclass.h as also noted on the docs:
The state property is declared in UIGestureRecognizer.h as being read-only. This property declaration is intended for clients of gesture recognizers. Subclasses of UIGestureRecognizer must import UIGestureRecognizerSubclass.h. This header file contains a redeclaration of state that makes it read-write.
I found that this is required for a double tap gesture but not for a single tap gesture:
doubleTapGestureRecognizer.delaysTouchesEnded = NO;

Programmatically initiate hitTest:WithEvent after gesture recognizer was canceled and return a different UIView to respond to events

I am overriding hitTest:withEvent to return self (the bottom most view)-
When returning self - my view will respond to touch events in turn initiating gesture recognizers.
If a gesture is canceled or some set of conditions happened - I want to manually initiate hitTest:withEvent and then return a different view to take care of the same sequence of events/touches that occurred. This is necessary as a gesture recognizer only initiates after hitTest:withEvent returns the gestures view and its state changed to began.
I am not sure how to do this - I thought about manually calling on my subviews
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
}
But I don't have the event parameter (The gesture received it)
I think this could not be done, pass touch event to UIGestureRecognizer is private API. But you can pass touch event the bottom most view received to any view you like and do your own gesture recognize.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView* selectView = [self _findMatchView];
// maybe convert touches to selectView coordinate
[selectView handleTouchBegan:touches withEvent:event];
}

why cant we detect a UITouch logic on UITableView, Objective C

I searched but not quite understand why we cant detect a UITouch on UITableView. What I am having right now is :a view controller with a table view located in its view. Please look at the picture below for your reference
In implementation class, I am enabling breakpoint for each UITouch methods which are
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
I notice that, these breakpoints are invoked if and only if you touch outside of the table view ( orange area )
I do not get it. I thought UITableView is subclass of UIScrollView which is subclass of UIView which is subclass of UIResponder. It means UITouch should be invoked. (correct me if I am wrong )
All comments are welcomed and appreciated here.
Rather than tampering with the table view, use a gesture recognizer. You can act as the delegate to ensure that all interactions work concurrently and enable and disable the gestures if / as required.
You can detect touches method in the UITableView by subclassing it as this:
I Test it and it print "Test" successfully
//TestTable.h
#import <UIKit/UIKit.h>
#interface TestTable : UITableView
#end
//TestTable.m
#import "TestTable.h"
#implementation TestTable
(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog("Test");
}
Tables utilize scroll views to handle panning, which use a pan gesture recognizer. Why not just tap into that?
CGPoint location = [self.tableView.panGestureRecognizer locationInView:self.tableView];
If you wish to detect the touches on UITableView, create a subclass of tableview and add implement UIResponder method, canBecomeFirstResponder.
#interface MyTableView: UITableView
#end
#implementation: MyTableView
- (BOOL) canBecomeFirstResponder{
return YES;
}
// then implement all other touch related methods, remember to call super in these methods
// such that it correctly forwards the events to other responders
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
}
#end

Trying to filter double-tap events through a UIView

In an iOS app, I have a UIWebView which I use to display some contents and to dismiss this UIWebView I use the following mechanism:
I put a DoubleTapView (subclass of UIView) on top of the UIWebView.
This DoubleTapView catches the double-tap events and executes adequate code to dimiss itself as well as the UIWebView.
Up to this point it works fine.
Here is the problem:
All the events that the UIWebView is interested in (scrolling the contents, tapping links ..etc..) don't go through; they are blocked by the DoubleTapView. How can I solve this?
In fact for what I just described I could have used a UIView instead of a DoubleTapView.
But I intended to subclass UIView with DoubleTapView. And then implement:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
or
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event;
to solve my problem, but I must be doing something wrong, because it does not work (at list the way I did).
Here is some code I wrote to put the parts together:
webView=[[UIWebView alloc] initWithFrame:varFrame];
webView.delegate=self;
quitGesture=[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(quitWeb:)];
quitGesture.numberOfTapsRequired=2;
varFrame.origin=CGPointZero;
quitSensor=[[DoubleTapView alloc] initWithFrame:varFrame];
quitSensor.userInteractionEnabled=YES;
[quitSensor addGestureRecognizer:quitGesture];
[webView addSubview:quitSensor];
and here is what I tried when implementing the methods for DoubleTapView:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[[self superview] touchesBegan:touches withEvent:event];
}
- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
[[self superview] motionBegan:motion withEvent:event];
}

Resources