6

I am doing some drag and rotation calculations using UIPanGestureRecognizer. The rotation angle is correct, and the drag location is almost correct. The problem is that as you go around the center of the box needs to be adjusted according to the angle and I can't figure out how.

I've included pictures of what a 180 rotation looks like but where the finger is during the rotation. I just don't know how to adjust to make the block stay with your finger appropriately. And heres a video just to clarify because it is strange behavior. http://tinypic.com/r/mhx6a1/5

EDIT: Here is a real world video of what should be happening. The problem being that in the iPad video your finger is moving where in the real world your finger would be cemented in a particular place on the item moving. The math needed is to adjust your touch location along the angle with a difference from the actual center. I just can't figure out the math. http://tinypic.com/r/4vptnk/5

first shot

second shot

third shot

Thanks very much!

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    if (gesture.state == UIGestureRecognizerStateBegan) {
        // set original center so we know where to put it back if we have to.
        originalCenter = dragView.center;

    } else if (gesture.state == UIGestureRecognizerStateChanged) {
        [dragView setCenter:CGPointMake( originalCenter.x + [gesture translationInView:self.view].x , originalCenter.y + [gesture translationInView:self.view].y )];

        CGPoint p1 = button.center;
        CGPoint p2 = dragView.center;

        float adjacent = p2.x-p1.x;
        float opposite = p2.y-p1.y;

        float angle = atan2f(adjacent, opposite); 

        [dragView setTransform:CGAffineTransformMakeRotation(angle*-1)];

    }
}
Ryan Poolos
  • 18,421
  • 4
  • 65
  • 98
  • The video was very useful, thanks for doing that. Is the block supposed to be "rotating" around the info button as if the info button were a stick in the ground, such that you're looking for essentially collision detection at the stick? If that's the case, then the math you're looking for is more about the distance between the edge of the rectangle and the outer edge of the info button. I may have some code that can help with that given the arbitrary position of the block as you push it around with your finger. – tobinjim Apr 16 '12 at 22:25
  • This is just a small test project of a feature that will be a in a larger project. the info button represents a small round UIButton. The block represents what will be a UIImageView rect. It should rotate around and always face the button. The problem is that while the user drags the block the finger is getting misplaced which leads up unnatural feeling movement. I need a way to keep the finger in the same place on the block. I assume it has to do with adjusting the center based on the angle and touch distance from the original center. I just can't figure out the math. – Ryan Poolos Apr 16 '12 at 23:07
  • So the question is this: Is the UIImageView supposed to remain "in contact" the round UIButton? I have code that kept what could be thought of as a tooltip exactly 20 pixels off to the side of a shape that was rotating, so with a rectangle that was rotating the tooltip had to "slide out" away from the rectangle's center as a corner came by, and then slide back in closer to center as the narrow side of the rectangle was moving by. – tobinjim Apr 17 '12 at 04:38
  • Another question: Is there a reason to be doing this with a UIPanGestureRecognizer rather than just touchesMoved:? I opted for a pan on a different project because I wanted velocity, but I don't see that that's important in your project. – tobinjim Apr 17 '12 at 04:43
  • The rectangle should not be bound to the button except by rotation. It should be able to drag anywhere on the screen. I just kept it close in the video to emphasize how the finger was getting off track. And I opted for UIPanGestureRecognizer because thats what I was asked to use. However its only a math problem, touchesMoved wouldn't changed the outcome. – Ryan Poolos Apr 17 '12 at 11:50
  • Your code is keeping the center of the rectangle equidistant from the touch point at all times, regardless of the orientation of the rectangle. Your video started out with the finger at the bottom of the long side of the rectangle, so by the time the rectangle is horizontal there is visually greater separation even though the center-to-touch delta is the same. Is there a real-world analogy to help understand how the rectangle should be moving considering its orientation change? (I realize you may be trying to obscure trade secrets) – tobinjim Apr 17 '12 at 17:02
  • Real world imagine a block tied to a string so its always pointed at a point. You place your finger on it and drag it around. Your finger touched the block would never change. In the video your finger is changing however, even going off the block. I've updated my answer with a video of a real world example. – Ryan Poolos Apr 17 '12 at 21:28
  • Ryan, I've played with a bunch of variations on this and come to the conclusion that because in the real world there is a physical connection between the object being pushed and the center object, we can't model that on the iPad -- for all approaches there exists the case where you push from a point far enough out from the center of the dragView to cause the rotation of the dragView to move it out from under your finger. When I constrain the rotation of the dragView to keep the touchPt under the finger, the "point at" relationship to the center object can't be honored. – tobinjim Apr 18 '12 at 16:01

2 Answers2

8

I've finally solved this issue and have it working perfectly. Persistence am I right??

Here is the code for the solution with a few comments to explain the changes.

- (void)handlePan:(UIPanGestureRecognizer *)gesture
{
    if (gesture.state == UIGestureRecognizerStateBegan) {
        // Get the location of the touch in the view we're dragging.
        CGPoint location = [gesture locationInView:dragView];

        // Now to fix the rotation we set a new anchor point to where our finger touched. Remember AnchorPoints are 0.0 - 1.0 so we need to convert from points to that by dividing
        [dragView.layer setAnchorPoint:CGPointMake(location.x/dragView.frame.size.width, location.y/dragView.frame.size.height)];


    } else if (gesture.state == UIGestureRecognizerStateChanged) {
        // Calculate Our New Angle
        CGPoint p1 = button.center;
        CGPoint p2 = dragView.center;

        float adjacent = p2.x-p1.x;
        float opposite = p2.y-p1.y;

        float angle = atan2f(adjacent, opposite); 

        // Get the location of our touch, this time in the context of the superview.
        CGPoint location = [gesture locationInView:self.view];

        // Set the center to that exact point, We don't need complicated original point translations anymore because we have changed the anchor point.
        [dragView setCenter:CGPointMake(location.x, location.y)];

        // Rotate our view by the calculated angle around our new anchor point.
        [dragView setTransform:CGAffineTransformMakeRotation(angle*-1)];

    }
}

Hope my month+ struggle and solution helps someone else in the future. Happy Coding :)

Ryan Poolos
  • 18,421
  • 4
  • 65
  • 98
  • 1
    Thanks for posting the answer to your own question once you've figured it out.Much appreciated. It's just really helped me. – Litome Feb 04 '16 at 11:38
3

Based on touch events https://github.com/kirbyt/KTOneFingerRotationGestureRecognizer

Helped me to solve similar problem