Getting the UITouch objects for a UIGestureRecognizer - objective-c

Is there a way to get the UITouch objects associated with a gesture? UIGestureRecognizer doesn't seem to have any methods for this.

Jay's right... you'll want a subclass. Try this one for size, it's from one of my projects. In DragGestureRecognizer.h:
#interface DragGestureRecognizer : UILongPressGestureRecognizer {
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
#end
#protocol DragGestureRecognizerDelegate <UIGestureRecognizerDelegate>
- (void) gestureRecognizer:(UIGestureRecognizer *)gr movedWithTouches:(NSSet*)touches andEvent:(UIEvent *)event;
#end
And in DragGestureRecognizer.m:
#import "DragGestureRecognizer.h"
#implementation DragGestureRecognizer
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
if ([self.delegate respondsToSelector:#selector(gestureRecognizer:movedWithTouches:andEvent:)]) {
[(id)self.delegate gestureRecognizer:self movedWithTouches:touches andEvent:event];
}
}
#end
Of course, you'll need to implement the
- (void) gestureRecognizer:(UIGestureRecognizer *)gr movedWithTouches:(NSSet*)touches andEvent:(UIEvent *)event;
method in your delegate -- for example:
DragGestureRecognizer * gr = [[DragGestureRecognizer alloc] initWithTarget:self action:#selector(pressed:)];
gr.minimumPressDuration = 0.15;
gr.delegate = self;
[self.view addGestureRecognizer:gr];
[gr release];
- (void) gestureRecognizer:(UIGestureRecognizer *)gr movedWithTouches:(NSSet*)touches andEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
self.mTouchPoint = [touch locationInView:self.view];
self.mFingerCount = [touches count];
}

If it is just the location you are interested in, you do not have to subclass, you will be notified of location changes of the tap from the UIGestureRecognizer.
Initialize with target:
self.longPressGestureRecognizer = [[[UILongPressGestureRecognizer alloc] initWithTarget: self action: #selector(handleGesture:)] autorelease];
[self.tableView addGestureRecognizer: longPressGestureRecognizer];
Handle UIGestureRecognizerStateChanged to get location changes:
- (void)handleGesture: (UIGestureRecognizer *)theGestureRecognizer {
switch (theGestureRecognizer.state) {
case UIGestureRecognizerStateBegan:
case UIGestureRecognizerStateChanged:
{
CGPoint location = [theGestureRecognizer locationInView: self.tableView];
[self infoForLocation: location];
break;
}
case UIGestureRecognizerStateEnded:
{
NSLog(#"Ended");
break;
}
default:
break;
}
}

If you only need to find out the location of the gesture, you can call either locationInView: or locationOfTouch:inView: on the UIGestureRecognizer object. However if you want to do anything else, then you'll need to subclass.

If you're writing your own UIGestureRecognizer you can get the touch objects overriding:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
or the equivalent moved, ended or canceled
The Apple docs has lots of info on subclassing

Here is a method to get a long press added to an arbitrary UIView.
This lets you run longpress on any number of UIViews.
In this example I restrict access to the UIView method through tags but you could use isKindOfClass as well.
(From what I found you have to use touchesMoved for LongPress because touchesBegan fires before the LongPress becomes active)
subclass - .h
#import <UIKit/UIKit.h>
#import <UIKit/UIGestureRecognizerSubclass.h>
#interface MyLongPressGestureRecognizer : UILongPressGestureRecognizer
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
#property (nonatomic) CGPoint touchPoint;
#property (strong, nonatomic) UIView* touchView;
#end
subclass - .m
#import "MyLongPress.h"
#implementation MyLongPressGestureRecognizer
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
{
UITouch * touch = [touches anyObject];
self.touchPoint = [touch locationInView:self.view];
self.touchView = [self.view hitTest:[touch locationInView:self.view] withEvent:event];
}
#pragma mark - Setters
-(void) setTouchPoint:(CGPoint)touchPoint
{
_touchPoint =touchPoint;
}
-(void) setTouchView:(UIView*)touchView
{
_touchView=touchView;
}
#end
viewcontroller - .h
//nothing special here
viewcontroller - .m
#import "ViewController.h"
//LOAD
#import "MyLongPress.h"
#interface ViewController ()
//lOAD
#property (strong, nonatomic) MyLongPressGestureRecognizer* longPressGesture;
#end
#implementation PDViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//LOAD
self.longPressGesture =[[MyLongPressGestureRecognizer alloc] initWithTarget:self action:#selector(longPressHandler:)];
self.longPressGesture.minimumPressDuration = 1.2;
[[self view] addGestureRecognizer:self.longPressGesture];
}
//LOAD
-(void) setLongPressGesture:(MyLongPressGestureRecognizer *)longPressGesture {
_longPressGesture = longPressGesture;
}
- (void)longPressHandler:(MyLongPressGestureRecognizer *)recognizer {
if (self.longPressGesture.touchView.tag >= 100) /* arbitrary method for limiting tap */
{
//code goes here
}
}

Simple and fast:
NSArray *touches = [recognizer valueForKey:#"touches"];
Where recognizer is your UIGestureRecognizer

Related

how to add 3dtouchforce to UIButton?

I want to measure the touch force when tapping or dragging a button. i have created a UITapGestureRecognizer (for tapping) and added it to myButton like this:
UITapGestureRecognizer *tapRecognizer2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(buttonPressed:)];
[tapRecognizer2 setNumberOfTapsRequired:1];
[tapRecognizer2 setDelegate:self];
[myButton addGestureRecognizer:tapRecognizer2];
i have created a method called buttonPrssed like this:
-(void)buttonPressed:(id)sender
{
[myButton touchesMoved:touches withEvent:event];
myButton = (UIButton *) sender;
UITouch *touch=[[event touchesForView:myButton] anyObject];
CGFloat force = touch.force;
forceString= [[NSString alloc] initWithFormat:#"%f", force];
NSLog(#"forceString in imagePressed is : %#", forceString);
}
I keep getting zero values (0.0000) for touch. any help or advice would be appreciated. i did a search and found DFContinuousForceTouchGestureRecongnizer sample project but found it too complicated. I use iPhone 6 Plus s that has touch. i can also measure touch when tapping on any other area in the screen but not on buttons using this code:
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
//CGFloat maximumPossibleForce = touch.maximumPossibleForce;
CGFloat force = touch.force;
forceString= [[NSString alloc] initWithFormat:#"%f", force];
NSLog(#"forceString is : %#", forceString);
}
You are getting 0.0000 in buttonPressed because the user already lifted his finger when this is called.
You are right, that you need to get the force in the touchesMoved method, but you need to get it in the UIButton's touchesMoved method. Because of that you need to subclass UIButton and override its touchesMoved method:
Header file:
#import <UIKit/UIKit.h>
#interface ForceButton : UIButton
#end
Implementation:
#import "ForceButton.h"
#implementation ForceButton
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGFloat force = touch.force;
CGFloat relativeForce = touch.force / touch.maximumPossibleForce;
NSLog(#"force: %f, relative force: %f", force, relativeForce);
}
#end
Also, there is no need to use a UITapGestureRecognizer to detect a single tap on a UIButton. Just use addTarget instead.

Creating a draggable image view

I have been searching the net for a long time and I have not found a concrete way of making an image view draggable. Here is what I have so far:
tempViewController.h
#import <UIKit/UIKit.h>
#import "MyRect.h"
#class UIView;
#interface tempViewController : UIViewController
#property (nonatomic, strong) MyRect *rect1;
#end
tempViewController.m
#import "tempViewController.h"
#interface tempViewController ()
#end
#implementation tempViewController
#synthesize rect1 = _rect1;
- (void)viewDidLoad
{
[super viewDidLoad];
_rect1 = [[MyRect alloc]initWithFrame:CGRectMake(150.0, 100.0, 80, 80)];
[_rect1 setImage:[UIImage imageNamed:#"cloud1.png"]];
[_rect1 setUserInteractionEnabled:YES];
[self.view addSubview:_rect1];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches]anyObject];
if([touch view] == _rect1)
{
CGPoint pt = [[touches anyObject] locationInView:_rect1];
NSLog(#"%#",NSStringFromCGPoint(pt));
_rect1.center = pt;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches]anyObject];
if([touch view] == _rect1)
{
CGPoint pt = [[touches anyObject] locationInView:_rect1];
NSLog(#"%#",NSStringFromCGPoint(pt));
_rect1.center = pt;
}
}
#end
MyRect right now is an empty UIImageView Class.
Dragging the image from a point such as [532,589] on micron moves it to a totally different part of the screen such as [144, 139]
Just attach a UIPanGestureRecognizer to your view. In the recognizer's action, update your view's center based on the “translation” (offset) of the recognizer, then reset the recognizer's translation to zero. Here's an example:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *draggableView = [[UIView alloc] initWithFrame:CGRectMake(150, 100, 80, 80)];
draggableView.userInteractionEnabled = YES;
draggableView.backgroundColor = [UIColor redColor];
[self.view addSubview:draggableView];
UIPanGestureRecognizer *panner = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(panWasRecognized:)];
[draggableView addGestureRecognizer:panner];
}
- (void)panWasRecognized:(UIPanGestureRecognizer *)panner {
UIView *draggedView = panner.view;
CGPoint offset = [panner translationInView:draggedView.superview];
CGPoint center = draggedView.center;
draggedView.center = CGPointMake(center.x + offset.x, center.y + offset.y);
// Reset translation to zero so on the next `panWasRecognized:` message, the
// translation will just be the additional movement of the touch since now.
[panner setTranslation:CGPointZero inView:draggedView.superview];
}
Don't use -touchesBegan:withEvent: etc.
What you want to use for such "high-level" things are UIGestureRecognizers.
You would add a pan gesture recognizer to your view set a delegate (possibly the view itself), and in the callback move the view by the distance the recognizer moved.
You either remember the initial position and move by the -translationInView each time, or simply move by the translation and then use -setTranslation:inView: to reset the recognizers translation to zero, so on the next call of the delegate method, you will again get the movement since the last call.

How to access a class instance from another method

Here's my code:
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(50.0, 50.0, 0, 0)];
[self.view addSubview:view1];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if([touch.view isEqual:self]) {
CGPoint point = [[touches anyObject] locationInView:self];
NSLog(#"%#",NSStringFromCGPoint(point));
self.view1.center = point;
}
}
I want to be able to access the instance "view1" of the class "UIView" from the touchesMoved method.
Thanks in advance!
You can declare local variables as stated in the other answer. However, in my opinion its better to declare private properties in the anonymous category as below:
#import "ViewController.h"
#interface ViewController ()
#property (nonatomic, strong) UIView *view1; // declares view1 as a private property
#end
#implementation ViewController
#synthesize view1 = _view1;
- (void)viewDidLoad
{
[super viewDidLoad];
self.view1 = [[UIView alloc] initWithFrame:CGRectMake(50.0, 50.0, 0, 0)];
[self.view addSubview:self.view1];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [[event allTouches] anyObject];
if([touch.view isEqual:self])
{
CGPoint point = [[touches anyObject] locationInView:self];
NSLog(#"%#",NSStringFromCGPoint(point));
self.view1.center = point;
}
}
You can make rect1 an instance variable by placing it in your .h file. You can do something like:
#interface tempViewController : UIViewController
{
myRect *rect1;
}
Then in your .m file you will not have to declare it again.

SubClassing UIButton but I can't get "addTarget:action:forControlEvents:" to execute?

I have subclassed UIButton to draw graphics, but the "addTarget" won't work.
- (void)addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
I did not do anything with making adjustments to that method.
The Button graphics work ok on the "touches".
If I use a standard UIButton then the "addTarget" works fine.
Not sure what I am missing??
thx
#import <UIKit/UIKit.h>
#interface CButton : UIButton
{
CGContextRef c;
int myState;
}
#end
#import "CButton.h"
#implementation CButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
//NSLog(#"init state: %d", self.state);
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// myState = 1;
myState = !myState;
// NSLog(#"BeginState: %d", self.state);
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
// myState = 0;
// NSLog(#"EndState: %d", self.state);
// [self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
#end
You have to call super from your event handlers when you're all done handling events. How else will the button know of the touches to call your action?
e.g.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// myState = 1;
myState = !myState;
// NSLog(#"BeginState: %d", self.state);
[self setNeedsDisplay];
[super touchesBegan:touches withEvent:event]; // you need this
}
That being said, the comments on your original post are right, subclassing UIButton can get tricky. But it looks as if you have things under control (mostly). Good luck!

how to execute function from first class by second class object placed in main class?

I've got 3 classes. ViewController, WorkspaceView, MainMenuView.
How to execute -(void)showMenu from -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; placed in WorkspaceView.m (when object workspaceView initialized in ViewController.m is clicked)? mainMenuView and workspaceView are created in ViewController class.
I hope you understand ;)
thanks for help.
ViewController.h
#import <UIKit/UIKit.h>
#import "WorkspaceView.h"
#import "MainMenuView.h"
#interface ViewController : UIViewController
{
WorkspaceView *workspaceView;
MainMenuView *mainMenuView;
}
#property (nonatomic, retain) WorkspaceView *workspaceView;
#property (nonatomic, retain) MainMenuView *mainMenuView;
#end
ViewController.m
#import "ViewController.h"
#implementation ViewController
#synthesize workspaceView;
#synthesize mainMenuView;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
// WorkspaceView
workspaceView = [[WorkspaceView alloc] initWithFrame:CGRectZero];
[self.view addSubview:workspaceView];
// ---
// MainMenuView
mainMenuView = [[MainMenuView alloc] initWithFrame:CGRectZero];
[self.view addSubview:mainMenuView];
// ---
...
}
...
#end
WorkspaceView.h
#import <UIKit/UIKit.h>
#interface WorkspaceView : UIView
#end
WorkspaceView.m
#import "WorkspaceView.h"
#implementation WorkspaceView
...
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
//howto execute showMenu or hideMenu from MainMenuView using mainMenuView object in ViewController?
NSLog(#"workspace"); // <-it's work;
}
#end
MainMenuView.h
#interface MainMenuView : UIView
...
-(void)hideMenu;
-(void)showMenu;
#end
MainMenuView.m
#import "MainMenuView.h"
#implementation MainMenuView
- (id)initWithFrame:(CGRect)frame{
...
}
-(void)hideMenu{
self.frame = CGRectMake(0, -1 * self.frame.size.height, self.frame.size.width, self.frame.size.height);
}
-(void)showMenu{
self.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
}
#end
I'll show you the way to do it with the Notification Center. You could also use delegates and devise a Protocol, but this is a very simple approach.
In WorkspaceView.m:
#import "WorkspaceView.h"
#implementation WorkspaceView
...
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self];
//howto execute showMenu or hideMenu from MainMenuView using mainMenuView object in ViewController?
[[NSNotificationCenter defaultCenter] postNotificationName:#"SHOW_MENU" object:nil];
NSLog(#"workspace"); // <-it's work;
}
#end
in MainMenuView.m:
#import "MainMenuView.h"
#implementation MainMenuView
- (id)initWithFrame:(CGRect)frame{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleShowMenu:)
name:#"SHOW_MENU" object:nil];
}
- (void)handleShowMenu:(NSNotification*)note
{
[self showMenu];
}
-(void)hideMenu{
self.frame = CGRectMake(0, -1 * self.frame.size.height, self.frame.size.width, self.frame.size.height);
}
-(void)showMenu{
self.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
}
#end