iOS: Transfer ownership of UIView while touches happening?
Asked Answered
I

2

7

The moment I receive touchesBegan, I want to removeFromSuperview the view that was touched and addSuperview to a new parent view, and then continue to receive touches. However I am finding that sometimes this does not work. Specifically, touchesMoved and touchesEnded are never called.

Is there a trick for making this work correctly? This is for implementing a drag and drop behavior, where the view is initially inside a scroll view.

Thanks.

Ilocano answered 5/4, 2012 at 15:12 Comment(0)
A
7

Instead of:

[transferView removeFromSuperView];
[newParentView addSubview:transferView];

Use only:

[newParentView addSubview:transferView];

The documentation states: "Views can have only one superview. If view already has a superview and that view is not the receiver, this method removes the previous superview before making the receiver its new superview."

Therefore there is no need to use removeFromSuperView because it is handled by addSubview. I have noticed that removeFromSuperView ends any current touches without calling touchesEnded. If you use only addSubview, touches are not interrupted.

Arm answered 25/2, 2013 at 4:37 Comment(2)
This should be the accepted answer as it is not necessary to manage dragging in the superview to achieve the OP's goal. Simply removing removeFromSuperView does the trick. Thanks walapu.Bambino
Great answer, this was very helpful!Hearsh
O
0

You need to process your touches in the superview instead of in the view that you want switched out. This will allow you to switch out the view without loosing your touch events. When you do this though, you'll have to test yourself whether the touch is occurring in the specific subview you want switched out. This can be done many ways, but here are some methods to get you started:

Converting Rects/Point to another view:

[view convertRect:rect toView:subview];
[view convertPoint:point toView:subview];

Here are some methods to test if the point is located in the view:

[subView hitTest:point withEvent:nil];
CGRectContainsPoint(subview.frame, point); //No point conversion needed
[subView pointInside:point withEvent:nil];

In general, it's better to use UIGestureRecognizers. For example, if you were using a UIPanGestureRecognizer, you would create a method that the gesture recognizer can call and in that method you do your work. For example:

- (void) viewPanned:(UIPanGestureRecognizer *)pan{
    if (pan.state == UIGestureRecognizerStateBegan){
        CGRect rect = subView.frame;
        newView = [[UIView alloc] initWithFrame:rect];
        [subView removeFromSuperview];
        [self addSubview:newView];
    } else if (pan.state == UIGestureRecognizerStateChanged){
        CGPoint point = [pan locationInView:self];
        newView.center = point;
    } else {
        //Do cleanup or final view placement
    }
}

Then you init the recognizer, assign it to the target (usually self) and add it:

[self addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(viewPanned:)]];

Now self (which would be the superview managing it's subviews) will respond to pan motions.

Oenomel answered 5/4, 2012 at 15:24 Comment(4)
Not sure why that would be. Can you post code? BTW, what's keeping you from using a UIGestureRecognizer? In almost every case I've considered using Touches, I've found that either an existing UIGestureRecognizer or subclassing UIGestureRecognizer works better (and I can reuse it).Oenomel
If you're using the stock UIPanGestureRecognizer, touchesBegan shouldn't be in your code. You should setup some target method like this: - (void) viewPanned:(UIPanGestureRecognizer)pan. When you init the recognizer you set its target: [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(viewPanned:)]. That method will get called repeatedly and you can check the state and perform the appropriate actions: If (pan.state == UIGestureRecognizerStateBegan){ .......} or If (pan.state == UIGestureRecognizerStateChanged{ ..... } etc...Oenomel
I expanded my answer to reflect this.Oenomel
I notice that you're removing the view and creating another to add to the target. I am definitely finding that when I remove the original from the superview, it ceases to receive touches, even if I re-add it to another view immediately afterwards.Ilocano

© 2022 - 2024 — McMap. All rights reserved.