Search code examples
iosdrag-and-dropuigesturerecognizerautolayoutsubviews

iOS >> Dragged View is Jumping Back to Original Position >> Auto Layout Combined with UIPanGestureRecognizer Issue


I have several UIViews that the user should be able to drag & drop. I use UIPanGestureRecognizer to manage the d&d action (see code below). The d&d action is working fine, but when I start dragging another UIView, the one that I just dropped is jumping back to its original position.

- (void)handlePan:(UIPanGestureRecognizer *)recognizer {

    CGPoint translation = [recognizer translationInView:self.view];

    switch (recognizer.state) {
        case UIGestureRecognizerStateBegan:
            for (UIView* checkView in dragAndDropImageViewsArray) {
                if (checkView == recognizer.view) {
                    [checkView.superview bringSubviewToFront:checkView]; //this is done to make sure the currently dragged object is the top one
                }
            }
            break;

        case UIGestureRecognizerStateChanged:
            recognizer.view.center = CGPointMake(recognizer.view.center.x + translation.x, recognizer.view.center.y + translation.y);
            break;

        case UIGestureRecognizerStateEnded:
            NSLog(@"UIGestureRecognizerStateEnded");
            break;

        default:
            break;
    }

    [recognizer setTranslation:CGPointMake(0, 0) inView:self.view];

}

I tried several things that "sloved" the problem but caused other problems:

  • Cancel Auto-Layout: That totally solves it but now I have to start calculate and manage object locations and sizes for different devices and orientations.
  • Use layer.zPosition instead of bringSubviewToFront: that makes objects appear above other objects while apparently avoiding the "jump back", but does not allow me to change the hierarchy between the different draggable objects.

I started deepening my understanding about Auto Layout, but it gets very complicated when it seems to me that what I'm looking for should be very simple; I saw many methods that deal with performing a "re-auto-layouting", but I'm not clear about how to work with them.

Is there a simple solution here? like calling a method that overrides the IB Auto-Layout constrains and redefine them according to the UIView new position?

Any assistance would be very musch appreciated.


Solution

  • I think that if you are using autolayout and you want to change the position of a view, you need to be updating the constraints on the view instead of manually setting the center of a view. From the description it sounds like you are setting the center, but when the app next lays out the views (eg you drag another) it re-sets the center to whatever autolayout says it should be.

    I'm not sure what the rest of your app looks like, but you may be able to find nearby views and add constraints to it, or you might prefer to just update the constraints of the dragged view and set the leading space and top space to the superview manually. If you've got a collection handy you could also clear all the constraints and then re-add them as you'd like to keep padding in between the views.

    To set the constraints in code you can clear the constraints for the moved view and re-add them, or if you have a view in IB you can drag the constraints into code to create an outlet. Look at the removeConstraints and addConstraints methods to do it in code.

    Lastly, if it works how you'd like without autolayout, you could try setting translatesAutoresizingMaskIntoConstraints = YES on the moved views, but be aware that may cause conflicts elsewhere.