apply 3d animation to view - iphone

I'm struggling with animations and can't find anything like i want. I would like to apply a 3d rotation to a view. Like a cube rotation, the view should have two images, and rotate from one to other like iphone notification animation. Animation i want is like this one here: http://www.youtube.com/watch?v=pBgVbzBJqDc
I don't think even one way of how to do this, so any help would be very appreciated!
Thanks in advance!

You can apply a 3D animation to a view by setting a CATransform3D with the transform on the views layer. The rotation will look very flat without any perspective so you should set an appropriate value for the third row and forth column of the transform matrix.
ObjC:
CATransform3D rotation = CATransform3DIdentity;
rotation.m34 = -1.0/500.0;
rotation = CATransform3DRotate(rotation, M_PI, 1.0, 0.0, 0.0);
myView.layer.transform = rotation;
Swift:
var rotation = CATransform3DMakeRotation(CGFloat(M_PI), 1.0, 0.0, 0.0);
rotation.m34 = CGFloat(-1.0/500.0)
firstView.layer.transform = rotation;
You would give the two views suitable values for the angle and position them where they should be on screen.
To make it look like a cube rotation you could also translate the layers half the width of the cubes side (so that it doesn't rotate around the center) before rotating the transform matrix.
rotation = CATransform3DTranslate(rotation, 0.0, 0.0, sideOfCube/2.0);

Related

How can I transform a UIView from being parallel to the ground to being perpendicular?

I want to transform a view from a plane that is parallel to the ground to a plane that is perpendicular to the ground.
Any ideas?
To rotate a view in 3D, you need to modify the transform property of the view's layer.
It sounds like you want to rotate the view around the X axis. And I guess you want perspective, where the edge closer to the viewer appears larger and the edge farther from the viewer appears smaller.
We'll write a helper function that makes the transform. We start with the identity transform:
static CATransform3D transformWithAngle(CGFloat angle) {
CATransform3D transform = CATransform3DIdentity;
Then set the m34 component of the transform based on the distance from the viewer to the view:
transform.m34 = -1.0f / 300.0f;
If you use a number larger than 300, the perspective will be weaker. If you use a number smaller than 300, the perspective will be stronger.
Next, add the rotation:
transform = CATransform3DRotate(transform, angle, 1, 0, 0);
Then return the transform:
return transform;
}
Let's say you want to apply this to a view named self.riseView. You probably want the rotation to be around the bottom edge of the view, so set the layer's anchor point to the bottom edge:
- (void)viewDidLoad {
CGRect frame = self.riseView.frame;
self.riseView.layer.anchorPoint = CGPointMake(0.5f, 1.0f);
self.riseView.frame = frame;
and initialize the layer's transform with an angle of 0:
self.riseView.layer.transform = transformWithAngle(0);
}
You can make the view “set” (rotate down to the ground plane) by rotating it to an angle of -M_PI_2:
- (IBAction)setButtonWasTapped:(id)sender {
[self animateRiseViewToAngle:-M_PI_2];
}
and you can make it “rise” (rotate up) by rotating it to an angle of zero:
- (IBAction)riseButtonWasTapped:(id)sender {
[self animateRiseViewToAngle:0];
}
Here's how we actually do the animation:
- (void)animateRiseViewToAngle:(CGFloat)angle {
[UIView animateWithDuration:2 animations:^{
self.riseView.layer.transform = transformWithAngle(angle);
}];
}

Curving/warping views with CoreAnimation or OpenGL for carousel effect

Right now I'm populating a UIScrollView with a series of views. The views need to be warped to make the UIScrollView appear like a carousel. In other words when the user scrolls it needs to be like a circle. I've never done anything quite like this before, but I'm assuming CoreAnimation is out of the question and OpenGL needs to be used. If this is possible with CoreAnimation or Quartz then I really just need a sample on how to warp the views and I can figure the rest out myself but I'm not familiar with OpenGL.
If you want to warp the views, you'll either need OpenGL or you could use Core Animation's CAShapLayer which allows you to specify a bezier path which can have this curve in it. But keep in mind that this curving you're seeing is likely just an optical illusion (though in your image above it looks like an actual curve). If you get enough rectangles with the correct y axis rotation in a row, I think you can come up with the effect you're looking for with straight Core Animation. I'm pretty sure that's how things are implemented in the Core Animation demos Apple provided a couple years ago. Here's a screenshot from the video from that presentation:
I messed around with the transform of a view's layer a little bit and came up with this:
- (IBAction)sliderDidChange:(id)sender
{
CGFloat value = [(UISlider*)sender value];
CGFloat xOff = value - 0.5;
CATransform3D trans = CATransform3DIdentity;
trans.m34 = 1.0f / -1000.0f;
trans = CATransform3DRotate(trans, degreesToRadians(xOff * -25.0f), 0.0f, 1.0f, 0.0f);
trans = CATransform3DTranslate(trans, 0.0f, 0.0f, 900.0f * fabs(xOff));
[[frameView layer] setTransform:trans];
CGPoint center= [frameView center];
[frameView setCenter:CGPointMake(1024.0 * value, center.y)];
}
I threw together a demo project that shows how the rotation works in response to a slider. It doesn't use a scroll view so you would have to adapt it, but I think you can track the current scroll offset and apply the transform accordingly. Not sure if it will help but there it is.
In my experience, it is a bit tricky to get the values right. The way to give a view perspective is by manipulating it's layer transform. I have used the following method to achieve the transfor for a similar effect:
-(CATransform3D)makeTransformForAngle:(CGFloat)angle from:(CATransform3D)start{
CATransform3D transform = start;
// the following two lines are the key to achieve the perspective effect
CATransform3D persp = CATransform3DIdentity;
persp.m34 = 1.0 / -1000;
transform = CATransform3DConcat(transform, persp);
transform = CATransform3DRotate(transform,angle, 0.0, 1.0, 0.0);
return transform;
}
This was done to create a "flip page" animation, so you may need to adapt. To use it, do the following:
flip_page.layer.transform = [self makeTransformForAngle:angle from:CATransform3DIdentity];
where flip_page is a UIView. Cheers!

Quartz 2D Path rotation

I'm fairly new to Quartz 2D.
Imagine the following scenario:
You have a circle-shaped mini map view.
I'm drawing triangle (the arc isn't important right now) on top of the map. This shape represents the visible area.
I need to have the triangle shape rotate along the mini map as the user changes orientation.
Currently this how the path is being drawn:
CGAffineTransform transform = CGAffineTransformMakeRotation(angleInRadians);
CGPath visibleAreaPath = CGPathCreateMutable();
CGPathMoveToPoint(visibleAreaPath, &transform, miniMapCenter.x, miniMapCenter.y);
CGPathAddLineToPoint(visibleAreaPath, &transform, 18.0, 8.0);
CGPathAddLineToPoint(visibleAreaPath, &transform, 66.0, 8.0);
CGPathCloseSubpath(visibleAreaPath);
I then draw the path using a CAShapeLayer like so:
CALayer *rootLayer = [visibleAreaView layer];
visibleAreaShape = [CAShapeLayer layer];
[visibleAreaShape setFillColor:[UIColor colorWithHue:0.584 saturation:0.8 brightness:0.9 alpha:0.6].CGColor];
[visibleAreaShape setFillRule:kCAFillRuleNonZero];
[visibleAreaShape setAnchorPoint:CGPointMake(0.5, 0.5)];
[rootLayer addSublayer:visibleAreaShape];
[visibleAreaShape setPath:visibleAreaPath];
The path is being rotated, but not based on a given origin.
Keep in mind that setting the layer's anchor point doesn't help me since what I want is to rotate the path (ultimately I wouldn't even need to display it, since I will be using it to determine which points are visible on the mini map).
Any ideas on how to accomplish this?
Thank you.
I would suggest you store the points in polar form and convert to a path when needed. It's very easy to rotate in polar coordinates (simply change the theta value).

Question about rotating a slider

I am working on an application which needs to rotate the slider at one end. However, by using CGAffineTransformMakeRotation(), I can only rotate the slider at the centre.
What should I do in order to rotate the slider at the end point? If possible, please give a short paragraph of sample code. Thanks a lot.
I rotate a UIImageView by setting the anchorPoint property for the view's layer. e.g.
myView.layer.anchorPoint = CGPointMake(0.5, 1.0);
and then applying a CATransform3DRotate as the layer's transform property
CATransform3D rotationTransform = CATransform3DIdentity;
rotationTransform = CATransform3DRotate(rotationTransform, positionInRadians, 0.0, 0.0, 1.0);
myView.layer.transform = rotationTransform;
I found this from Apple's metronome example, so it's worth checking that out. Hope that helps
I second cidered's answer. To do what you want using CGAffineTransforms, you have to compose transformations until you get the effect you're looking for:
CGAffineTransform transform = CGAffineTransformMakeRotation(1.0);
transform = CGAffineTransformTranslate(transform, slider.frame.size.width / 2, 0.0);
slider.transform = transform;

How can I rotate a UIImageView with respect to any point (other than its center)?

By default, a UIImageView will rotate only about its center. How do I get it to rotate about any other point in the image?
One way of doing this is by changing the anchorPoint of the UIImageView's underlying layer and rotating about that. You can change the point of rotation using something like the following:
imageView.layer.anchorPoint = CGPointMake(0.25, 0.25);
anchorPoint is defined in terms of relative coordinates within the layer. That is, (0,0) is the upper-left of the layer (on the iPhone, where UIView layers have flipped Y coordinates) and (1,1) is the lower-right.
Moving the anchorPoint may move your image, so you might need to adjust its position afterwards.
To rotate the image's layer about that new anchor point, you can change the CATransform3D struct for the layer using something like the following:
CATransform3D rotatedTransform = imageView.layer.transform;
rotatedTransform = CATransform3DRotate(rotatedTransform, 60.0 * M_PI / 180.0, 0.0f, 0.0f, 1.0f);
imageView.layer.transform = rotatedTransform;
This example does an incremental rotation of 60 degrees about the anchor point.
All changes to a layer are animated by default. You can disable this animation by enclosing your changes to these properties in a CATransaction like the following:
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
// Code for changes here
[CATransaction commit];