CATransform3DMakeRotation and shadow - ios

I'm creating the flip animation library.
In two words, I create the CALayer and rotate it using CoreAnimation's CATransform3DMakeRotation.
The question is – is there a way to add a shadow to that scene? Without rewriting the whole code with OpenGL :)

Have a look at CAGradientLayer. I am currently using it to add shadows in a similar situation. May be costly performance-wise (still have to check that), but looks quite convincing.
Add CAGradientLayer as sublayer(s) to your layers and animate its opacity. You may have to play around a little with the gradient stops and colors to get them right.

I am not sure about OpenGL but have you checked CATransform3D
Add
#define DEGREES_TO_RADIANS(d) (d * M_PI / 180)
in .pch file
CATransform3D myTransform = CATransform3DIdentity;
myTransform.m34 = 1.0 / -500;
myTransform = CATransform3DRotate(myTransform, DEGREES_TO_RADIANS(90), 0.0f, 0.0f, 1.0f);
myView.layer.transform = myTransform;
you can go on changing the angle here DEGREES_TO_RADIANS(90)
Here you can add shadow to myView.

Related

CATransform3D making my UITextView disappear?

So I have a UITextView that's supposed to be visually sitting on the bottom edge of the screen and then stretching up and back "into" the screen, "Star Wars" opening crawl-style.
After much googling etc, I feel like I have what looks like the right code for the job... but instead of setting up the perspective view I was looking for, this is just making the UITextView totally disappear!
The text view is set up in a storyboard with springs/struts (no autolayout) such that it's pinned at the top and bottom of the main view, about 20px in from each side, and the springs are active in both directions. Its outlet is hooked up to self.infoTextView. It shows up as I'd expect if I don't apply any transformations to it.
But when i fire off the code below in viewDidLoad, the text view just disappears completely. I'm sure I'm missing something but I can't seem to figure out what t is.
CGRect frame = self.infoTextView.layer.frame;
self.infoTextView.layer.anchorPoint = CGPointMake(.5f, 1.0f);
self.infoTextView.layer.frame = frame;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, 1.57, 0, 1, 0);
self.infoTextView.layer.transform = rotationAndPerspectiveTransform;
thanks!
There are two things you'll need to modify in your code:
The rotation axis. You're rotating it around the Y axis. To get the
effect you're after you will need to change the rotate transform to
turn around the X axis.
To get "Star Wars opening crawl-style" effect you'll need to set a negative angle or a negative perspective.
Also, you would probably want to set a more "strong" perspective to achieve a more dramatic effect.
Here's an example based on your transform code:
CGFloat angle = -45;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 200;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, angle / 180.0 * M_PI, 1, 0, 0);
self.infoTextView.layer.transform = rotationAndPerspectiveTransform;

How to make a curved progress bar around a circular UIView?

I want to make a circular progress bar such as that, how to animate its resizing and properties (shape, colour, width, etc.) as well.
I am trying to make it around a circular transparent view.
Where to start?
Does anyone has a sample code?
There's no substitute for learning, however a little help never goes a miss, so here are snippets of code that will accomplish the task for you.
The concept is to use a CAShapeLayer and a UIBezierPath and progress is simply setting the strokeEnd propertie of the UIBezierPath. You'll need to declare a CAShapeLayer and set its properties. We'll call this our progressLayer. (i'm not going to provide complete code, simply direction and samples for you to put together.)
// setup progress layer
// N.B borderWidth is a float representing a value used as a margin.
// pathwidth is the width of the progress path
// obviously progressBounds is a CGRect specifying the Layer's Bounds
[self.progressLayer setFrame:progressBounds];
UIBezierPath *progressPath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)) radius:(bounds.size.height - self.borderWidth - self.pathWidth ) / 2 startAngle: (5* -M_PI / 12) endAngle: (2.0 * M_PI - 7 * M_PI /12) clockwise:YES];
self.progressLayer.strokeColor = [UIColor whiteColor].CGColor;
self.progressLayer.lineWidth = 6.0f ;
self.progressLayer.path = progressPath.CGPath;
self.progressLayer.anchorPoint = CGPointMake(0.5f, 0.5f);
self.progressLayer.fillColor = [UIColor clearColor].CGColor;
self.progressLayer.position = CGPointMake(self.layer.frame.size.width / 2 - self.borderWidth / 2, self.layer.frame.size.height / 2 - self.borderWidth/2);
[self.progressLayer setStrokeEnd:0.0f];
You will obviously need to add progressLayer to your view hierarchie
Then you will need a simple animation to progress the bar;
// updateInterval is a float specifying the duration of the animation.
// progress is a float storing the, well, progress.
// newProgress is a float
[self.progressLayer setStrokeEnd:newProgress];
CABasicAnimation *strokeEndAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
strokeEndAnimation.duration = updateInterval;
[strokeEndAnimation setFillMode:kCAFillModeForwards];
strokeEndAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
strokeEndAnimation.removedOnCompletion = NO;
strokeEndAnimation.fromValue = [NSNumber numberWithFloat:self.progress];
strokeEndAnimation.toValue = [NSNumber numberWithFloat:newProgress];
self.progress = newProgress;
[self.progressLayer addAnimation:strokeEndAnimation forKey:#"progressStatus"];
in your image above, the un-progressed path is nothing more than a second fully stroked layer behind the progressLayer
oh, and one final point, you'll find that the Circle progresses Clockwise. If you take the time to learn what's happening here, you'll figure out how to set progress Anti Clockwise.
Good Luck ...
You can also check out my lib MBCircularProgressBar, it also shows how to animate with and how to make it customizable from the Interface Builder. You can make it transparent by using transparent colors.
http://raw.github.com/matibot/MBCircularProgressBar/master/Readme/example.jpg
Here's a great, short tutorial on how to make this: http://www.tommymaxhanks.com/circular-progress-view/
You will need to learn a bit of Core Graphics in order to accomplish this in the way you want (the author used a gradient in the implementation, you may want to change that.), but it's worth it if you want to do cool stuff like this on the iPhone!
I think even more helpful is the example code, which is hosted here: https://github.com/mweyamutsvene/THControls/tree/master/CircularProgressView

core gore graphics and alpha values

I am draw some circle on a map with
CGContextFillEllipseInRect
This works great, but the only issue is that I have the alpha value set to .5
If tow or more circle overlap the the overlap area is darker because of the two alpha values being added I suppose. Is there an easy way to maintain the same alpha value regardless if the circle overlap?
Thanks
If you're working with closed shapes and drawing them all in the same drawrect (or at least in the same context) you can accomplish what you want by using UIBezierPath.
// create a path with a circle
UIBezierPath* path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50) radius:50.0f startAngle:0 endAngle:M_PI * 2.0f clockwise:YES];
// add another circle to the path
[path appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:50.0f startAngle:0 endAngle:M_PI * 2.0f clockwise:YES]];
// draw them both, since they are in a single path they will be treated as a single shape
[path fill];
Draw the circles at full opacity, and set the opacity of the view/layer to 0.5.
EDIT:
To clarify, I assume you know how to stop drawing your circles at half opacity, since you managed to set it up that way in the first place. So make the opacity of your circles be 1.0. Then, set your view's opacity to 0.5 by doing the following:
view.alpha = 0.5;
I'm assuming you have a UIView subclass, so this can either be in the place in your code where you create the view or it could be inside the subclass in your init method for it. So if you're doing it from within the UIView subclass, it would be self instead of view:
self.alpha = 0.5;
Also, this will make all of the drawing you do in this view be 0.5 opacity. If you want to have some content that's full opacity and some that's half opacity, the simplest solution I can give you is to have two different views, one for the half opacity content, the other for the full opacity content. There are better ways to accomplish this, but for the sake of communicating easily a solution, this is probably easiest.

Manipulating UIView layer's CATransform3D and UIView's CGAffineTransform at the same time

This might sound like a weird question, but what I am trying to do isn't very strange. I am currently resizing a UIView via the view's CGAffineTransform like this:
self.selectedController.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, resizeRatioValue, resizeRatioValue);
Where the resizeRatioValue is a value between 0.7-1.0, depending on the location of the gesture. This works great. With a pan gesture, I am able to shrink down my view beautifully.
Now, I would like to add another twist to this. As the view is shrinking, I would like to apply a rotation to the view (similar to the coverflow effect) so that it rotates and shrinks at the same time.
I can rotate the view just fine using this code:
float angle = 45.0 * progressRatio;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 2000; // Perspective
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform,
1 * angle / (180.0 / M_PI), 0.0f, 1.0f, 0.0f);
self.selectedController.view.layer.transform = rotationAndPerspectiveTransform;
But when I put both of these together, it's one or the other. Whichever one occurs second is the one that works. I can't get them to coexist.
What can I do to make it so that these can both work together? Or is there a completely different approach that would be better suited for what I am trying to do?

Apply a Transform to a UIView Itself

I'm trying to apply a perspective transform to my UIView object with the following code:
CATransform3D t = CATransform3DIdentity;
t.m34 = -1.0/1000;
t = CATransform3DRotate(t, angle, 0.0f, 1.0f, 0.0f);
myView.layer.transform = t;
and I don't see any effect at all. I tried other transforms like a simple translation and they don't work either.
However, if I do either of the following two modifications then it will work somewhat but neither satisfies my request:
Change the last line to
myView.layer.sublayerTransform = t;
This sort of works but it only transforms the subviews on myView, not myView itself.
Add an animation code to apply the change instead of directly assign the change to the layer:
CABasicAnimation *turningAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
turningAnimation.toValue = [NSValue valueWithCATransform3D:t];
turningAnimation.delegate = self;
turningAnimation.fillMode = kCAFillModeForwards;
turningAnimation.removedOnCompletion = NO;
[myView.layer addAnimation:turningAnimation forKey:#"turning"];
The thing is that I don't want the animation.
Can anybody point a direction for me?
Thanks!
You should be able to just use myView.transform = CGAffineTransformMakeRotation(angle). It won't animate the property unless you explicitly tell it to using [UIView animateWithDuration:]. Using the UIView transform property will ultimately apply your transformation to the CALayer that backs UIView, so I would make sure to only interact with the transforms on one level (UIView or CALayer).
UIView transform doc:
https://developer.apple.com/library/ios/documentation/uikit/reference/uiview_class/UIView/UIView.html#//apple_ref/occ/instp/UIView/transform
CGAffineTransform Doc:
https://developer.apple.com/library/ios/documentation/GraphicsImaging/Reference/CGAffineTransform/Reference/reference.html#//apple_ref/c/func/CGAffineTransformMakeRotation
I figured out the problem after test by Krishnan confirmed that layer.transform itself does work without the aid of animation or sublayerTransform. The problem I encountered was caused by an animation that had been applied to my view's layer and was not subsequently removed. Since this animation had a toValue equal to the identity transform, I didn't realize it can actually prevent subsequent non-animated transforms from working. Setting removedOnCompletion = YES (which is default) on the animation solved the mystery.

Resources