Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using UIPanGestureRecognizer to rotate a UIView around a certain point

I'm trying to create an animation that allows a user to pull out a panel from the side of the window. A small amount of the view will be visible at the upper-right, and by pulling it, the view will rotate outwards, pinned to the bottom right of the screen. Hopefully these images show what I'd like to achieve (the sheet of paper on the right indicates that portion of the view would be hidden. I'd like the user to be able to drag it, as opposed to swiping or tapping.

Can anyone point me in the right direction for how I might achieve this? I'm pretty new to animations in iOS. I've gotten the pan gesture working, but I need this point at which to rotate about. I can set the view's .center property, but doing so just moves the whole view to that point, so I need a center to rotate from, which isn't actually the middle of the view, I think?

enter image description hereenter image description here

Edit: I've written a method that's working, but the view isn't "stuck" to the user's finger. I can drag my finger around, say, 45 degrees, but the view will have moved more than that. Not sure how I can sync them up, any ideas?

- (void) swipeSidebarOpen: (UIPanGestureRecognizer *) recognizer {
    //[self openOrCloseSideBar: YES];
    _sidebarView.layer.anchorPoint = CGPointMake(1.0, 1.0);
    _sidebarView.layer.position = CGPointMake(510, 330);

    UIView *shuttle = [recognizer view];

    if ([recognizer state] == UIGestureRecognizerStateBegan || [recognizer state] == UIGestureRecognizerStateChanged) {
        CGPoint vel = [recognizer velocityInView:[recognizer view]];

        if (vel.x > 0) {
            [recognizer view].transform = CGAffineTransformRotate([[recognizer view] transform], M_PI / 80.0f);
            [recognizer setTranslation: CGPointZero inView:[shuttle superview]];
        } else {
            [recognizer view].transform = CGAffineTransformRotate([[recognizer view] transform], -M_PI / 80.0f);
            [recognizer setTranslation: CGPointZero inView:[shuttle superview]];
        }
    }
}
like image 352
Luke Avatar asked Nov 30 '12 11:11

Luke


2 Answers

A bit late but I wrote some code to handle pan, zoom and scale for one fingered use. Standard gestures lack some accuracy for fine tuned work or bouncing around in the back of a cab.

These two things may help you a bit. The rotation gesture target method is able to accept from the pinch recognizer or from the pan. You need to track where the first touch point falls for the pan recognizer though because you will calculate an angle between its current point and the start point of the gesture. You also need to save the screen center. For me I put all of these points into the window's co-ordinate system, so you have to pick which one you want to use and stick with it.

CGFloat /* midpoint, startpoint, point */
CGPointAngleATan2(CGPoint mp, CGPoint sp, CGPoint p)
{
    CGFloat angle = 0;
    CGFloat sAngle = atan2(sp.y-mp.y,sp.x-mp.x);
    CGFloat pAngle = atan2(p.y-mp.y,p.x-mp.x);
    angle = pAngle-sAngle;
    return angle;
}


- (CGFloat)calculateRotation:(UIGestureRecognizer *)sender;
{
    CGFloat rotation;

    if ([sender respondsToSelector:@selector(rotation)]){
        rotation = [(UIRotationGestureRecognizer *)sender rotation];
    } else {
        // convert to window co-ordinates, app specific so change if needed
        CGPoint point = [sender locationInView:self.view.window];
        rotation = CGPointAngleATan2(screenCenter,firstTouch,point);
    }
    return rotation;
}
like image 193
dbquarrel Avatar answered Nov 10 '22 16:11

dbquarrel


By default the view will rotate arounds its center but you can change that by setting another anchorPoint for the views layer. (You will need to import Core Animation to do that (QuartzCore.framework)).

The anchor point is normalised (both X and Y goes from 0 to 1). By default it is in the center (0.5, 0.5) but you want to change it to the lower right corner (1.0, 1.0).

myView.layer.anchorPoint = CGPointMake(1.0, 1.0);

Changing the anchorPoint will visually move your view on screen so that the position of the views layer in the superview is the same point that the view rotates around.

myView.layer.position = theFixedHerePoint; // The point you are showing in your drawing...

For example: if the superview goes from (0,0) to (100, 100) then the position of myView should be the point where the lower right corner should be, not where the center of myView is. Like in the illustration below (X is the anchorPoint (1, 1) and the position (95, 95)). Note that 95 was just an arbitrary value.

┌───────────────────┐
│ superview         │
│                   │
│      ┌──────────┐ │
│      │ myView   │ │
│      │          │ │
│      │          │ │
│      └──────────X │
└───────────────────┘

Now when you pan you can simply set an appropriate rotational transform and the view will rotate around its lower right corner.

like image 1
David Rönnqvist Avatar answered Nov 10 '22 18:11

David Rönnqvist