Question

I am facing problem in my project my purpose of project is to add many UIImages on the starting and these images should be drag and drop able, for that I uses UIPanGestureRecognizer but still facing problem please help me out Thanks in advance.

this code is in my viewdidload method

 UIView *holderView = [[UIView alloc]init];

for (int i = 0; i<=4; i+=1) {

    NSMutableArray * images = [[NSMutableArray alloc]initWithObjects:@"iphone-status-bar.png",@"uiswitch.png",@"NavigationBar.png",@"barbutton.png",@"ios-back-button.png", nil];


    NSString * image_name= [NSString stringWithFormat:@"%@",[images objectAtIndex:i]];

//  NSLog(@"%@",image_name);

    UIImage *image = [UIImage imageNamed:image_name];

    holderView = [[UIView alloc] initWithFrame:CGRectMake(2, i*30, image.size.width, image.size.height)];

    UIImageView *imageview = [[UIImageView alloc] initWithFrame:[holderView frame]];

    imageview.userInteractionEnabled = YES;

    [imageview setImage:image];

    [holderView addSubview:imageview];

    UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(move:)];

    [panRecognizer setMinimumNumberOfTouches:1];

    [panRecognizer setMaximumNumberOfTouches:1];

    [panRecognizer setDelegate:self];

     [holderView addGestureRecognizer:panRecognizer];


     [self.view addSubview:holderView];
}

And the gesture handler is:

-(void)move:(id)sender {

    [self.view bringSubviewToFront:[(UIPanGestureRecognizer*)sender view]];
    CGPoint translatedPoint = [(UIPanGestureRecognizer*)sender translationInView:self.view];

    if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateBegan) {

        firstX = [[sender view] center].x;
        firstY = [[sender view] center].y;
    }

    translatedPoint = CGPointMake(firstX+translatedPoint.x, firstY+translatedPoint.y);

    [[sender view] setCenter:translatedPoint];

    if([(UIPanGestureRecognizer*)sender state] == UIGestureRecognizerStateEnded) {

        CGFloat finalX = translatedPoint.x + (.01*[(UIPanGestureRecognizer*)sender velocityInView:self.view].x);
        CGFloat finalY = translatedPoint.y + (.01*[(UIPanGestureRecognizer*)sender velocityInView:self.view].y);

        if(UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation])) {

            if(finalX < 0) {

                finalX = 0;
            }

            else if(finalX > 768) {

                finalX = 768;
            }

            if(finalY < 0) {

                finalY = 0;
            }

            else if(finalY > 1024) {

                finalY = 1024;
            }
        }

        else {

            if(finalX < 0) {

                finalX = 0;
            }

            else if(finalX > 1024) {

                finalX = 768;
            }

            if(finalY < 0) {

                finalY = 0;
            }

            else if(finalY > 768) {

                finalY = 1024;
            }
        }

        [UIView beginAnimations:nil context:NULL];
        [UIView setAnimationDuration:.35];
        [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
        [[sender view] setCenter:CGPointMake(finalX, finalY)];
        [UIView commitAnimations];
    }
}
Was it helpful?

Solution

I personally suspect the holderView logic. You're adding holderView to your controller's top level view using the vertical offset of i * 30, but when you're adding the imageView to the holderView, you're offsetting it within holderView by i * 30 again (because you're using the frame of holderView, which has a y offset in it)! Worse, because your gesture recognizer is on the holderView and not the imageView, when you try to grab the imageView by tapping on the image, you're often going to not be over the holderView you thought you were, but rather one for one of the other images!

Assuming you like the way the images are laid out right now (really 60.0 points apart), then I'd suggest you change that i*30 to i*60.0 and either:

  • change the setting of frame for the imageView to [holderView bounds]; or, even better,

  • get rid of the holderView views altogether because (at least on the basis of what you've shared so far) it adds no value and only complicates the code.

As an aside, a few other observations:

  • The images array doesn't need to be mutable, and you shouldn't be reinitializing it inside the for loop.

  • You probably should reference [images count] rather than hard coding 4 in your for loop.

With these changes, the viewDidLoad code then looks like:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    NSArray *images = [[NSArray alloc]initWithObjects:@"iphone-status-bar.png",@"uiswitch.png",@"NavigationBar.png",@"barbutton.png",@"ios-back-button.png", nil];

    // if you're using a recent version of the compiler, you could simplify that further to just
    //
    // NSArray *images = @[@"iphone-status-bar.png",@"uiswitch.png",@"NavigationBar.png",@"barbutton.png",@"ios-back-button.png"];

    for (int i = 0; i < [images count]; i++) {

        NSString *imageName = [images objectAtIndex:i]; // or you could say `NSString *imageName = images[i];`
        UIImage *image = [UIImage imageNamed:imageName];
        UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
        imageView.frame = CGRectMake(2.0, i * 60.0, image.size.width, image.size.height);
        imageView.userInteractionEnabled = YES;
        [self.view addSubview:imageView];

        UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(move:)];
        panRecognizer.minimumNumberOfTouches = 1;
        panRecognizer.maximumNumberOfTouches = 1;

        [imageView addGestureRecognizer:panRecognizer];
    }
}

You can simplify your move routine a bit, too:

-(void)move:(UIPanGestureRecognizer *)gesture {

    static CGPoint originalCenter; // or make this a class ivar; but don't make this a global!

    if (gesture.state == UIGestureRecognizerStateBegan) {

        [gesture.view.superview bringSubviewToFront:gesture.view];
        originalCenter = gesture.view.center;
    }

    CGPoint translation = [gesture translationInView:self.view];
    CGPoint translatedPoint = CGPointMake(originalCenter.x+translation.x, originalCenter.y+translation.y);

    gesture.view.center = translatedPoint;

    if(gesture.state == UIGestureRecognizerStateEnded) {
        CGPoint velocity = [gesture velocityInView:gesture.view];
        translatedPoint.x = MAX(translatedPoint.x + .01 * velocity.x, 0.0);
        translatedPoint.y = MAX(translatedPoint.y + .01 * velocity.y, 0.0);

        if(UIDeviceOrientationIsPortrait([[UIDevice currentDevice] orientation])) {

            translatedPoint.x = MIN(translatedPoint.x, 768.0);
            translatedPoint.y = MIN(translatedPoint.y, 1024.0);
        }
        else {

            translatedPoint.x = MIN(translatedPoint.x, 1024.0);
            translatedPoint.y = MIN(translatedPoint.y, 768.0);
        }

        [UIView animateWithDuration:0.35
                              delay:0.0
                            options:UIViewAnimationOptionCurveEaseOut
                         animations:^{
                             gesture.view.center = translatedPoint;
                         }
                         completion:nil];
    }
}

Personally, I hate seeing hard-coded coordinates, so I might further simplify the UIGestureRecognizerStateEnded code to something like:

if(gesture.state == UIGestureRecognizerStateEnded) {
    CGPoint velocity = [gesture velocityInView:gesture.view];
    translatedPoint.x = MAX(translatedPoint.x + .01 * velocity.x, 0.0);
    translatedPoint.y = MAX(translatedPoint.y + .01 * velocity.y, 0.0);
    translatedPoint.x = MIN(translatedPoint.x, gesture.view.superview.bounds.size.width);
    translatedPoint.y = MIN(translatedPoint.y, gesture.view.superview.bounds.size.height);

    [UIView animateWithDuration:0.35
                          delay:0.0
                        options:UIViewAnimationOptionCurveEaseOut
                     animations:^{
                         gesture.view.center = translatedPoint;
                     }
                     completion:nil];
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top