Using UIPanGestureRecognizer to rotate a UIView around a certain point
Asked Answered
E

3

6

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 here enter 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]];
        }
    }
}
Englebert answered 30/11, 2012 at 11:58 Comment(0)
H
1

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.

Halfbound answered 30/11, 2012 at 12:34 Comment(2)
Thanks, that's very helpful, I get that now. I've gotten that working, but I'm having an issue in that the rotation, although now around the right point), isn't "sticking" to the user's finger. I've added my method above, can you see what might be wrong with it? The ratio of how much I'm panning to how much it's moving is off.Englebert
Thank you very much for the nice explanation...my request is to you that can you please give us a complete code with little demonstration..??? @DavidSacculus
K
3

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;
}
Kamasutra answered 11/9, 2013 at 21:26 Comment(0)
H
1

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.

Halfbound answered 30/11, 2012 at 12:34 Comment(2)
Thanks, that's very helpful, I get that now. I've gotten that working, but I'm having an issue in that the rotation, although now around the right point), isn't "sticking" to the user's finger. I've added my method above, can you see what might be wrong with it? The ratio of how much I'm panning to how much it's moving is off.Englebert
Thank you very much for the nice explanation...my request is to you that can you please give us a complete code with little demonstration..??? @DavidSacculus
D
0

What you will need to do is as you are panning, in the handler for the pangesturerecognizer, you will need to apply a rotation transform for the view being panned. Like self.SomeView.transform = CGAffineTransformMakeRotation(some rotation). You will need to make some calculations for the amount of rotation to be applied. Likewise for setting a center.

Deprivation answered 30/11, 2012 at 12:8 Comment(2)
Could you expand on this a little? I'm struggling with working out the variables I'll be using when the user touches their finger down.Englebert
Dont really know any formula for this but what comes to mind is, as you pan from the right corner inwards, you will need to probably apply a rotation transform for the view being panned by an angle of measure between say the current point and the previous point. Not sure if im explaining this clearly, but say you have panned from point A to point B, the angle between the two points if u were to connect them to the origin is the angle u may need to rotate your view by.Deprivation

© 2022 - 2024 — McMap. All rights reserved.