Question

For an iOS app I'm working on, all the images on the users device are displayed on one portion of the screen. The user should be able to drag those images to another portion of the screen to have them added to the set of images that will be used in the memory game.

Here's a screenshot in case my explanation is hard to visualize A screenshot to make things more clear

When the user drags the image, what I would like to have happen is a slightly smaller semi-transparent version of that image is created that they drag down to the bottom, but the original image stays there. I can spawn the new image just fine and even assign a target that moves the new image. The problem I'm running into is in order to drag the new image the user has to lift their finger off the screen and start dragging again. I would like the new image to take the old images drag event over. This is the code I have right now.

UIControl *mask =  [[UIControl alloc]initWithFrame:frame];
[mask addSubview:img];
[mask addTarget:self action:@selector(dragExited:withEvent:) forControlEvents:UIControlEventTouchDragExit];
[scrImages addSubview:mask];


-(IBAction)dragExited:(id)sender withEvent:(UIEvent*)event
{
    UIControl *control = sender;
    [control resignFirstResponder];
    UIImageView *img = [control.subviews objectAtIndex:0];
    CGPoint point = [[[event allTouches]anyObject]locationInView:self.view];
    UIControl *mask = [[UIControl alloc]initWithFrame:CGRectMake(0, 0, img.image.size.width, img.image.size.height)];
    mask.center = point;
    [mask addSubview:[[UIImageView alloc]initWithImage:img.image]];
    [mask addTarget:mask action:@selector(removeFromSuperview) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:mask];
    [mask addTarget:self action:@selector(thumbnailMoved:withEvent:) forControlEvents:UIControlEventTouchDragInside];
    [mask touchesMoved:nil withEvent:nil];
}

-(IBAction)thumbnailMoved:(id)sender withEvent:(UIEvent*)event
{
    UIControl *control = sender;
    CGPoint point = [[[event allTouches]anyObject]locationInView:self.view];
    control.center = point;
}

I tried to start the dragging process by calling [mask touchesMoved:nil withEvent:nil]; but that doesn't seem to work. Any ideas?

Was it helpful?

Solution

I'm going to answer this with a different technique, pan gestures. I don't use your technique, so I can't speak to it, but I use the following all the time:

UIPanGestureRecognizer *pan;

pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanImage:)];
image01.userInteractionEnabled = YES;
[self.image01 addGestureRecognizer:pan];

pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanImage:)];
image02.userInteractionEnabled = YES;
[self.image02 addGestureRecognizer:pan];

And then I have the handler:

- (void)handlePanImage:(UIPanGestureRecognizer *)sender
{
    static CGPoint originalCenter;
    static UIImageView *imageview;

    if (sender.state == UIGestureRecognizerStateBegan)
    {
        UIImageView *selectedImageView = (UIImageView *) sender.view;
        imageview = [[UIImageView alloc] initWithFrame:selectedImageView.frame];
        imageview.image = selectedImageView.image;
        imageview.alpha = 0.8;
        [sender.view.superview addSubview:imageview];

        originalCenter = imageview.center;
    }
    else if (sender.state == UIGestureRecognizerStateChanged)
    {
        CGPoint translation = [sender translationInView:sender.view.superview];

        imageview.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y);
    }
    else if (sender.state == UIGestureRecognizerStateEnded)
    {
        [imageview removeFromSuperview];
        imageview = nil;

        CGPoint translation = [sender translationInView:sender.view.superview];

        // do whatever you want to at the end, in this case, animating the moving
        // of the old image to the new location. perhaps you don't want to move your
        // old image, but leave it there, maybe you just want to change the alpha of
        // what you dragged back to 1.0 and snap it into place. Just do whatever you
        // want here. Note, in this latter scenario, if you want to be able to drag
        // the new image back out, you'll have to create a new pan gesture recognizer
        // for it.

        [UIView animateWithDuration:0.2 
                         animations:^{
                             sender.view.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y);;
                         }];
    }
    else if (sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed)
    {
        [imageview removeFromSuperview];
        imageview = nil;
    }
}

You can also, rather than creating a new gesture recognizer for each image, just create one on the parent view, detect which image you're over, and then carry on. That might look like:

UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanSuperView:)];
[self.view addGestureRecognizer:pan];

And then:

- (void)handlePanSuperView:(UIPanGestureRecognizer *)sender
{
    static CGPoint originalCenter;
    static UIImageView *selectedImageView = nil;
    static UIImageView *draggedImageView = nil;

    if (sender.state == UIGestureRecognizerStateBegan)
    {
        selectedImageView = nil;
        CGPoint location = [sender locationInView:sender.view];
        for (UIImageView *imageview in _imageviews)
        {
            if (CGRectContainsPoint(imageview.frame, location))
                selectedImageView = imageview;
        }
        if (selectedImageView)
        {
            draggedImageView = [[UIImageView alloc] initWithFrame:selectedImageView.frame];
            draggedImageView.image = selectedImageView.image;
            draggedImageView.alpha = 0.8;
            [sender.view addSubview:draggedImageView];

            originalCenter = selectedImageView.center;
        }
    }
    else if (sender.state == UIGestureRecognizerStateChanged && selectedImageView)
    {
        CGPoint translation = [sender translationInView:sender.view];

        draggedImageView.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y);
    }
    else if (sender.state == UIGestureRecognizerStateEnded && selectedImageView)
    {
        [draggedImageView removeFromSuperview];
        draggedImageView = nil;

        CGPoint translation = [sender translationInView:sender.view];

        // do whatever you want to at the end, in this case, animating the moving of the old image

        [UIView animateWithDuration:0.2 
                         animations:^{
                             selectedImageView.center = CGPointMake(originalCenter.x + translation.x, originalCenter.y + translation.y);;
                         }];
    }
    else if ((sender.state == UIGestureRecognizerStateCancelled || sender.state == UIGestureRecognizerStateFailed) && selectedImageView)
    {
        [draggedImageView removeFromSuperview];
        draggedImageView = nil;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top