UIPanGestureRecognizer in a UITableViewCell not recognizing left Pan - ios

Right now I am developing an iPad app that uses a subclass of UITableviewCell that uses UIPanGestureRecognizer to slide left and right, However it only slides right and dosent slide left... This is the code I am usuing:
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
// Initialization code
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
pan.delegate = self;
[self addGestureRecognizer:pan];
}
return self;
}
This is my handlePan: method,
CGPoint translation = [gesture locationInView:self];
self.center = CGPointMake(self.center.x + translation.x,
self.center.y);
if (self.center.x > 700) {
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:self.tag],#"number",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"right" object:nil userInfo:dic];
dic = nil;
}
if (self.center.x < 0){
NSDictionary *dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:self.tag],#"number",nil];
[[NSNotificationCenter defaultCenter] postNotificationName:#"left" object:nil userInfo:dic];
dic = nil;
NSLog(#"Left Called");
}
[gesture setTranslation:CGPointZero inView:self];
No matter what I try, i cant seem to get the console to say "Left Called" , i.e. The cell slides left. I am really struggling with this issue and would appreciate any help at all.

The trick is to react to the changed state of the pan, and the center calculation can be much simpler...
- (void)handlePan:(UIPanGestureRecognizer *)gesture {
if ((gesture.state == UIGestureRecognizerStateChanged) ||
(gesture.state == UIGestureRecognizerStateEnded)) {
CGPoint newCenter = CGPointMake([gesture locationInView:self.superview].x, self.center.y);
self.center = newCenter;
// To determine the direction of the pan, check the sign of translation in view.
// This supplies the cumulative translation...
if ([gesture translationInView:self.superview].x > 0) {
NSLog(#">>>");
} else {
NSLog(#"<<<");
}
}
}

I think the way that you are determining left/right might be flawed.
CGPoint translation = [gesture locationInView:self];
self.center = CGPointMake(self.center.x + translation.x,
self.center.y);
self.center.x will always be positive since "translation" is the location in the view which is positive.
What you probably want to keep track of the original touch position and then compare that with the location when swiping/moving. Try something like this:
- (void)handlePan:(UIPanGestureRecognizer *)panRecognizer {
if (panRecognizer.state == UIGestureRecognizerStateBegan)
{
// create a local property for the original point and assign the position on first touch
self.originalPanTouchPoint = [panRecognizer locationInView:self.view];
// create a local property for cell's center prior to pan
self.initialCellCenter = self.center;
} else {
CGPoint currentTouchPoint = [panRecognizer locationInView:self.view];
CGFloat xTranslation = self.originalPanTouchPoint.x - currentTouchPoint.x;
self.center = CGPointMake(self.initialCellCenter.x + xTranslation, self.initialCellCenter.y);
}
}

Related

Gesture recognizer with Voice Over active

I developed an application which allows to the user to draw his finger signature in a canvas.
This feature is implemented using UIPanGestureRecognizer with a specific target action to draw a line in a UIView, but when the “Voice Over” is active the gesture recognizer action is not triggered anymore.
Gesture initialize code
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pan:)];
pan.maximumNumberOfTouches = pan.minimumNumberOfTouches = 1;
[self addGestureRecognizer:pan];
Gesture action code
- (void)pan:(UIPanGestureRecognizer *)pan {
CGPoint currentPoint = [pan locationInView:self];
CGPoint midPoint = midpoint(previousPoint, currentPoint);
if (pan.state == UIGestureRecognizerStateBegan)
{
[path moveToPoint:currentPoint];
}
else if (pan.state == UIGestureRecognizerStateChanged)
{
[path addQuadCurveToPoint:midPoint controlPoint:previousPoint];
}
previousPoint = currentPoint;
[self setNeedsDisplay];
}
Is there any way to draw a line in a view using gesture with “Voice Over” active?
Thanks and regards!
I resolved my problem setting both isAccessibilityElement and accessibilityTraits properties for UIView canvas:
canvasView.isAccessibilityElement = YES;
canvasView.accessibilityTraits = UIAccessibilityTraitAllowsDirectInteraction;

UIAttachmentBehavior doesn't work. UIDynamicAnimator

I am having trouble using initWithItem:attachedToItem: Initializes an attachment behavior that connects the center point of a dynamic item to the center point of another dynamic item.
But when I changed the center point of topview using method pan,only the topview moved around,I can't get the other view to move.Isn't it should be moving together?
(BTW I am trying to implement a pile of cards and move around all together when I pan the card in the top.)
-(void)pinch:(UIPinchGestureRecognizer *)gesture
{
if(gesture.state == UIGestureRecognizerStateChanged){
CGPoint pinchCen = [gesture locationInView:self.cardArea];
if (gesture.scale <= 0.5 && !self.pileStat) {
self.pileStat = !self.pileStat;
NSUInteger number = [self.cardViews count];
UIView *topView = [self.cardViews lastObject];
[topView addGestureRecognizer:[[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(pan:)]];
for (int i = 0; i < number;i++) {
UIView *cardView = self.cardViews[i];
[UIView animateWithDuration:0.5 animations:^{cardView.center = CGPointMake(pinchCen.x+i%10*0.5, pinchCen.y+i%10*0.5);} completion:^(BOOL finished){
if(i != number - 1){
UIAttachmentBehavior *attach = [[UIAttachmentBehavior alloc]initWithItem:cardView attachedToItem:topView];
[self.animator addBehavior:attach];
}
}];
}
}
else if(gesture.scale > 1.5 && self.pileStat)
{
self.pileStat = !self.pileStat;
}
}else if (gesture.state == UIGestureRecognizerStateEnded){
gesture.scale = 1.0f;
}
}
-(void)pan:(UIPanGestureRecognizer *)gesture
{
if (gesture.state == UIGestureRecognizerStateChanged || UIGestureRecognizerStateEnded) {
UIView *topView = [self.cardViews lastObject];
CGPoint trans = [gesture translationInView:self.cardArea];
topView.center = CGPointMake(trans.x+topView.center.x, trans.y+topView.center.y);
[gesture setTranslation:CGPointMake(0, 0) inView:self.cardArea];
}
}
setCenter does not play nice with UIDynamicAnimator. Instead of changing the center coordinate of your top view, you should use another UIAttachmentBehavior that you attach to your touch. Replace your pan gesture handler with this method:
//Add a variable in your interface or header section called "touchAttachment" of type UIAttachmentBehavior
- (void) pan: (UIPanGestureRecognizer *) sender{
UIView* topCard = [self.cardViews lastObject];
if (sender.state == UIGestureRecognizerStateBegan){
_touchAttachment = [[UIAttachmentBehavior alloc] initWithItem:topCard
attachedToAnchor: [sender locationInView:self.view]];
[self.animator addBehavior:_touchAttachment];
}
else if (sender.state == UIGestureRecognizerStateChanged){
[_touchAttachment setAnchorPoint: [sender locationInView: self.view]];
}
else if (sender.state == UIGestureRecognizerStateEnded){
[self.animator removeBehavior: _touchAttachment];
_touchAttachment = nil;
}
}
Make sure you add the "touchAttachment" variable.
Hope it helps :)

Cancel multiple touches on the View

I am adding UIPanGestureRecogniser to drag and drop images in a view.I want to drag only one image at a time.but I am able to drag two images at a time.which should not happen.
I am stuck with this bug since morning.I tried all the ways i found on google.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setMultipleTouchEnabled:NO];
for(UIImageView *iView in self.movableArray){
if ([iView isMemberOfClass:[UIImageView class]]){
UIPanGestureRecognizer * recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[iView addGestureRecognizer:recognizer];
[iView setUserInteractionEnabled:YES];
recognizer.delegate = self;
}
}
}
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
[gameView bringSubviewToFront:[(UIPanGestureRecognizer *)recognizer view]];
CGPoint translation = [recognizer translationInView:gameView];
recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x,
recognizer.view.center.y + translation.y);
[recognizer setTranslation:CGPointMake(0, 0) inView:gameView];
self.dragObjectImageView = (UIImageView*)recognizer.view;
if(recognizer.state == UIGestureRecognizerStateBegan){
int ind = [self.movableArray indexOfObject:self.dragObjectImageView];
for(int i = 0 ; i < [self.movableArray count] ; i ++){
if( i != ind ){
[[self.movableArray objectAtIndex:i] removeGestureRecognizer:recognizer];
}
}
self.homePosition = self.dragObjectImageView.frame;
}
if (recognizer.state == UIGestureRecognizerStateEnded) {
CGPoint touchPoint = [recognizer locationInView:gameView];
for (UIImageView *iView in self.staticArray) {
if ([iView isMemberOfClass:[UIImageView class]]) {
if (touchPoint.x > iView.frame.origin.x &&
touchPoint.x < iView.frame.origin.x + iView.frame.size.width &&
touchPoint.y > iView.frame.origin.y &&
touchPoint.y < iView.frame.origin.y + iView.frame.size.height)
{
self.dropTargetImageView = iView;
}
}
}
if(self.dragObjectImageView.tag == self.dropTargetImageView.tag){
self.dragObjectImageView.frame = CGRectMake(self.dropTargetImageView.frame.origin.x, self.dropTargetImageView.frame.origin.y + self.dropTargetImageView.frame.size.height/2 - 15, self.dragObjectImageView.frame.size.width, self.dragObjectImageView.frame.size.height);
[self.dragObjectImageView removeGestureRecognizer:recognizer];
}
}else{
self.dragObjectImageView.frame = self.homePosition;
}
}
}
This happens because you are adding one UIPanGestureRecognizer for each of your imageViews. Try adding only one to your self.view (and have your imageViews to setUserInteractionEnabled:NO, otherwise they will trap the touch). Also put
recognizer.maximumNumberOfTouches = 1;
Before you add it to your view. All you have to do now is to test which image view should be dragged in your handlePan method. You should check the recognizer state and when it turns to UIGestureRecognizerStateBegan you should save which imageView is being dragged. Then as the state is UIGestureRecognizerStateChanged just drag the view around. The most general way you can find out which view was touched (as i don't know your full view hierarchy) would be to do something like:
NSUInteger index = [self.movableArray indexOfObjectPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop)){
UIView* hitTest = (UIView*)obj;
return [hitTest pointInside:firstTouch withEvent:nil];
}];
if ( index != NSNotFound )
self.draggingView = self.moveableArray[index];
else
self.draggingView = nil;
Then of course if self.draggingView is nil you would do nothing when the user is panning around.
You should create a mechanism to setUserInteractionEnable = NO when your
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer;
Is called on the other images. You can either do that, or disable the others UIPanGestureRecognizer, like: myPanGestureRecognizer.enabled = NO;
A quick example:
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer
{
UIImageView *currentDraggedImageView = recognizer.view;
// Based on this, you can iterate again on your UIImageViews and disable them.
// Once your work is done with gesture recogniser, you can re-enable them.
}

Not able to move two images using uipangesturerecogniser

I want to move two images with same UIPanGestureRecognizer ,
I am able to move the first image, but as soon as I try to move the second image the first one goes back to its original position. I want the first image to retain it's after changed position.
-(void) viewWillAppear:(BOOL)animated
{
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanSuper:)];
[self.view addGestureRecognizer:pan];
}
- (void)handlePanSuper:(UIPanGestureRecognizer *)sender
{
static UIImageView *viewToMove;
static CGPoint originalCenter;
if (sender.state == UIGestureRecognizerStateBegan)
{
CGPoint location = [sender locationInView:self.view];
if (CGRectContainsPoint(self.imageView.frame, location))
{
viewToMove = imageView;
originalCenter = viewToMove.center;
}
else if (CGRectContainsPoint(self.image2.frame, location))
{
viewToMove = image2;
originalCenter = viewToMove.center;
}
else
{
viewToMove = nil;
}
if (viewToMove)
{
viewToMove.alpha = 0.8;
[viewToMove.superview bringSubviewToFront:viewToMove];
NSLog(#"hi i am being touched.");
}
}
if (sender.state == UIGestureRecognizerStateChanged && viewToMove != nil)
{
CGPoint translation = [sender translationInView:self.view];
viewToMove.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y);
}
else if ((sender.state == UIGestureRecognizerStateEnded ||
sender.state == UIGestureRecognizerStateFailed ||
sender.state == UIGestureRecognizerStateCancelled) && viewToMove != nil)
{
// do whatever post dragging you want, e.g.
// snap the piece into place
// CGPoint center = viewToMove.center;
//viewToMove.center = center;
viewToMove.alpha = 1.0;
viewToMove = nil;
}
}
You are setting your gesture recognizer in your superView. Why? You could set it inside the views that you are trying to move, it's easier and less likely to strange behaviors I edited your code, see below (not tested)
-(void) viewWillAppear:(BOOL)animated
{
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePanSuper:)];
self.imageView.userInteractionEnabled = self.image2.userInteractionEnabled = YES;
[self.imageView addGestureRecognizer:pan];
[self.image2 addGestureRecognizer:pan];
}
- (void)handlePanSuper:(UIPanGestureRecognizer *)sender
{
UIImageView *viewToMove = (UIImageView*)sender.view;
CGPoint originalCenter = viewToMove.center;
if (sender.state == UIGestureRecognizerStateBegan)
{
CGPoint location = [sender locationInView:self.view];
viewToMove.alpha = 0.8;
[viewToMove.superview bringSubviewToFront:viewToMove];
NSLog(#"hi i am being touched.");
}
else if (sender.state == UIGestureRecognizerStateChanged)
{
CGPoint translation = [sender translationInView:self.view];
viewToMove.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y);
}
else if ((sender.state == UIGestureRecognizerStateEnded ||
sender.state == UIGestureRecognizerStateFailed ||
sender.state == UIGestureRecognizerStateCancelled))
{
// do whatever post dragging you want, e.g.
// snap the piece into place
// CGPoint center = viewToMove.center;
//viewToMove.center = center;
viewToMove.alpha = 1.0;
viewToMove = nil;
}
}
Please try this, and let me know if worked.

Why is my UIPanGestureRecognizer not being detected and firing the method?

I have a UITableView, and I want to attach a UIPanGestureRecognizer to each of the cells, which are subclassed UITableViewCells - ArticleCells. In the awakeFromNib method I add the pan gesture recognizer, but it never fires. Why?
- (void)awakeFromNib {
[super awakeFromNib];
self.cellBack = [[CellBack alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, 80)];
[self.contentView addSubview:self.cellBack];
self.cellFront = [[CellFront alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, 80)];
[self.contentView addSubview:self.cellFront];
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(pannedCell:)];
panGestureRecognizer.delegate = self;
}
Which should be firing this method. But I put a breakpoint on it and it never does get fired.
- (void)pannedCell:(UIPanGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateBegan) {
_firstTouchPoint = [recognizer translationInView:self];
NSLog(#"fired");
}
else if (recognizer.state == UIGestureRecognizerStateChanged) {
NSLog(#"fired");
CGPoint touchPoint = [recognizer translationInView:self];
// Holds the value of how far away from the first touch the finger has moved
CGFloat xPos;
// If the first touch point is left of the current point, it means the user is moving their finger right and the cell must move right
if (_firstTouchPoint.x < touchPoint.x) {
xPos = touchPoint.x - _firstTouchPoint.x;
if (xPos <= 0) {
xPos = 0;
}
}
else {
xPos = -(_firstTouchPoint.x - touchPoint.x);
if (xPos >= 0) {
xPos = 0;
}
}
if (xPos > 10 || xPos < -10) {
// Change our cellFront's origin to the xPos we defined
CGRect frame = self.cellFront.frame;
frame.origin = CGPointMake(xPos, 0);
self.cellFront.frame = frame;
}
}
else if (recognizer.state == UIGestureRecognizerStateEnded) {
[self springBack];
}
else if (recognizer.state == UIGestureRecognizerStateCancelled) {
[self springBack];
}
}
And in the .h file I added so it would be notified as implementing it. But it never calls it as I said.
Why?
Did you do this?
[self.contentView addGestureRecognizer:panGestureRecognizer];

Resources