Different animation styles like flipboard animations - objective-c

I am new to ios development. i want different animation styles like flipboard animations.can any one give me some sample examples.
Thanks in advance

I don't have any experience in this, but after reading the docs, I think you're going to need a library for that. A quick Google search suggested:
Canvas (provides animations: bounce, slide, fade, fun)
MZFormSheetController
Pop
If you want to code your own custom animation, it appears that the apple documentation explains how.

You can use a library, but it is possible to transform the layer of a UIView in many ways, quite easily.
Say the view you want to animate is called view and is of type UIView
Here is how to animate it similar to a flip animation, where it rotates around the y-axis:
CALayer *layer = view.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform = CATransform3DTranslate(rotationAndPerspectiveTransform, 0, 0, 20);
rotationAndPerspectiveTransform.m34 = 5.0 / -500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, angle* M_PI / 180.0f, 0.0f, 1.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
The key function here is CATransform3DRotate which rotates the layer in 3d.
You specify the axis around which to rotate with the last 3 parameters (x, y, z) which is (0,1,0) in this case, i.e. the y-axis.
Note that this won't produce an animation, but rather, will orient the layer using the provided axis and angle.
To animate the layer, you will have to incrementally change the angle, in another function (e.g. using NSTimer).

Related

CAGradientLayer radial gradient

I used the following code to set gradient color:
NSArray *locations = #[#0.0, #0.1f];
CAGradientLayer *headerLayer = [CAGradientLayer layer];
headerLayer.colors = colors;
headerLayer.locations = locations;
How can I make radial gradient color?
I think I have to change locations, but I didn't guess the correct locations. Also, I tried this:
headerLayer.anchorPoint = CGPointMake(0.5, 1.0);
But it doesn't work.
Thanks.
By 2019, the accepted answer is erroneous and you can make a radial gradient by setting its type property to .radial.
On thing important is that the startPoint is the coordinate of the center of the ellipse or circle and the endPoint are the dimensions of the outer eclipse or circle. All coordinates have to be expressed in unit-based (aka 1.0 is 100%).
The following Swift example will draw an ellipse positioned at the center of the frame and the radiuses set to match the frame:
let g = CAGradientLayer()
// We want a radial gradient
g.type = .radial
g.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
let center = CGPoint(x: 0.5, y: 0.5)
g.startPoint = center
let radius = 1.5
g.endPoint = CGPoint(x: radius, y: radius)
The above example can be used to add a vignette to a view for example (see the image).
Note that the radius can be larger than the frame, and the center can be placed outside the frame as well by specifying coordinates greater than 1 or lower than -1.
you can't init radial CAGradientLayer, BUT you can easily modify current layer style to radial
layer.type = .radial
Update:
Seems like it does, other answers show the method.
Unfortunately, CAGradientLayer doesn't support radial gradients. The only way to do that is to subclass CALayer and do the drawing yourself.
I have answered the question here, but since this is an older question, the other one might be a duplicate.

Move vertices of a generic UIView

Is there any way to move the vertices of a UIView just like the images?
As a non-english native I really don't know the name of those points, so I'm using the term external point, but if you suggest the real name I'll edit it for sure.
If you are trying to create a 3D effect, you can apply 3D transforms to the CALayer of the UIView like this:
CATransform3D perspectiveMtx = CATransform3DIdentity;
perspectiveMtx.m34 = -1.0f / 100.0f;
CATransform3D rotMtx = CATransform3DRotate(perspectiveMtx, angle, 1.0f, 0.0f, 0.0f);
someView.layer.transform = rotMtx;
You may be able to simulate the distortion you want using 3D transforms, but if you really want a pure 2D distortion then you will probably have to use OpenGL directly to specify custom vertex positions.

iOS Core Animation Speed Up

I am trying to replace one complex view with another complex view using different animations, like moving to left/right/top/bottom. First and second views contains 30-40 subviews (buttons). My code is like this:
oldView.alpha = 1;
newView.alpha = 0;
oldView.frame = CGRectMake(0, 0, width, height);
newView.frame = CGRectMake(0, -height, width, height);
// begin animation
// setting duration 0.3
// ...
oldView.alpha = 0;
newView.alpha = 1;
oldView.frame = CGRectMake(0, height, width, height);
newView.frame = CGRectMake(0, 0, width, height);
// commit animation
// ...
It works nice on iPhone Simulator and on iPhone 4S, but it lags on iPhone 4. By lag I mean 12-15 FPS.
How can I speed up this animation?
Should I use center property instead of frame?
Should I render my views to UIImageView's and animate them?
Should I layout my views in UIScrollView and call scrollRectToVisible:animated:?
And please, explain me why my animation code so slow? When my two views contains 10-20 buttons - there are no problems with speed...
You will need to post more code as setting a new frame shouldn't be too much of a performance hit. There are however things you can do with your view's layer or in the drawRect: that would slow drawing and animation down considerably.
For instance:
myView.layer.shadowOffset = CGSizeMake(0.0, 10.0);
myView.layer.shadowRadius = 10;
myView.layer.shadowOpacity = 0.40;
Which adds a nice drop shadow to the view will kill the animation. So in this instance I turn off the shadows when animating and back on again when done.
Without seeing more of your code it is impossible to say where the problem lies.
EDIT:
To speed up the rendering of shadows you can set the shouldRasterize property to YES. This forces the layer to create a bitmap of the shadow rather than trying to redraw it every frame.
Try one of the following:
change center instead of frame
use transform property instead of frame. oldView.transform = CGAffineTransformMakeTranslation(0, height);
Just to add, if you are using explicit animations yet still setting animatable properties, you'll want to disable implicit animations by using:
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];

How do I achieve continuous rotation of an NSImage inside NSImageView?

FUTURE VIEWERS:
I have managed to finish this rotation animation and code with description can be found on tho question. NSImage rotation in NSView is not working
Before you proceed please up vote Duncan C 's answer. As I manage to achieve this rotation from his answer.
I have an image like this,
I want to keep rotating this sync icon, On a different thread. Now I tried using Quartz composer and add the animation to QCView but it is has very crazy effect and very slow too.
Question :
How do I rotate this image continuously with very less processing expense?
Effort
I read CoreAnimation, Quartz2D documentation but I failed to find the way to make it work. The only thing I know so far is, I have to use
CALayer
CAImageRef
AffineTransform
NSAnimationContext
Now, I am not expecting code, but an understanding with pseudo code will be great!
Getting an object to rotate more than 180 degrees is actually a little bit tricky. The problem is that you specify a transformation matrix for the ending rotation, and the system decides to rotate in the other direction.
What I've done is to create a CABasicAnimation of less than 180 degrees, set up to be additive , and with a repeat count. Each step in the animation animates the object more.
The following code is taken from an iOS application, but the technique is identical in Mac OS.
CABasicAnimation* rotate = [CABasicAnimation animationWithKeyPath: #"transform.rotation.z"];
rotate.removedOnCompletion = FALSE;
rotate.fillMode = kCAFillModeForwards;
//Do a series of 5 quarter turns for a total of a 1.25 turns
//(2PI is a full turn, so pi/2 is a quarter turn)
[rotate setToValue: [NSNumber numberWithFloat: -M_PI / 2]];
rotate.repeatCount = 11;
rotate.duration = duration/2;
rotate.beginTime = start;
rotate.cumulative = TRUE;
rotate.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
CAAnimation objects operate on layers, so for Mac OS, you'll need to set the "wants layer" property in interface builder, and then add the animation to your view's layer.
To make your view rotate forever, you'd set repeat count to some very large number like 1e100.
Once you've created your animation, you'd add it to your view's layer with code something like this:
[myView.layer addAnimation: rotate forKey: #"rotateAnimation"];
That's about all there is to it.
Update:
I've recently learned of another way to handle rotations of greater than 180 degrees, or continuous rotations.
There is a special object called a CAValueFunction that lets you apply a change to your layer's transform using an arbitrary value, including values that specify multiple full rotations.
You create a CABasicAnimation of your layer's transform property, but then instead of providing a transform, the value you supply is an NSNumber that gives the new rotation angle. If you provide a new angle like 20pi, your layer will rotate 10 full rotations (2pi/rotation). The code looks like this:
//Create a CABasicAnimation object to manage our rotation.
CABasicAnimation *rotation = [CABasicAnimation animationWithKeyPath:#"transform"];
rotation.duration = 10.0;
CGFLOAT angle = 20*M_PI;
//Set the ending value of the rotation to the new angle.
rotation.toValue = #(angle);
//Have the rotation use linear timing.
rotation.timingFunction =
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
/*
This is the magic bit. We add a CAValueFunction that tells the CAAnimation we are
modifying the transform's rotation around the Z axis.
Without this, we would supply a transform as the fromValue and toValue, and
for rotations > a half-turn, we could not control the rotation direction.
By using a value function, we can specify arbitrary rotation amounts and
directions and even rotations greater than 360 degrees.
*/
rotation.valueFunction =
[CAValueFunction functionWithName: kCAValueFunctionRotateZ];
/*
Set the layer's transform to it's final state before submitting the animation, so
it is in it's final state once the animation completes.
*/
imageViewToAnimate.layer.transform =
CATransform3DRotate(imageViewToAnimate.layer.transform, angle, 0, 0, 1.0);
[imageViewToAnimate.layer addAnimation:rotation forKey:#"transform.rotation.z"];
(I Extracted the code above from a working example application, and took out some things that weren't directly related to the subject. You can see this code in use in the project KeyframeViewAnimations (link) on github. The code that does the rotation is in a method called `handleRotate'

How to flip a UIView around the x-axis while simultaneously switching subviews

This question has been asked before but in a slightly different way and I was unable to get any of the answers to work the way I wanted, so I am hoping somebody with great Core Animation skills can help me out.
I have a set of cards on a table. As the user swipes up or down the set of cards move up and down the table. There are 4 cards visible on the screen at any given time, but only the second card is showing its face. As the user swipes the second card flips back onto its face and the next card (depending on the swipe direction) lands in it's place showing its face.
I have set up my card view class like this:
#interface WLCard : UIView {
UIView *_frontView;
UIView *_backView;
BOOL flipped;
}
And I have tried flipping the card using this piece of code:
- (void) flipCard {
[self.flipTimer invalidate];
if (flipped){
return;
}
id animationsBlock = ^{
self.backView.alpha = 1.0f;
self.frontView.alpha = 0.0f;
[self bringSubviewToFront:self.frontView];
flipped = YES;
CALayer *layer = self.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, M_PI, 1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
};
[UIView animateWithDuration:0.25
delay:0.0
options: UIViewAnimationCurveEaseInOut
animations:animationsBlock
completion:nil];
}
This code works but it has the following problems with it that I can't seem to figure out:
Only half of the card across the x-axis is animated.
Once flipped, the face of the card is upside down and mirrored.
Once I've flipped the card I cannot get the animation to ever run again. In other words, I can run the animation block as many times as I want, but only the first time will animate. The subsequent times I try to animate lead to just a fade in and out between the subviews.
Also, bear in mind that I need to be able to interact with the face of the card. i.e. it has buttons on it.
If anybody has run into these issues it would be great to see your solutions. Even better would be to add a perspective transform to the animation to give it that extra bit of realism.
This turned out to be way simpler than I thought and I didn't have to use any CoreAnimation libraries to achieve the effect. Thanks to #Aaron Hayman for the clue. I used transitionWithView:duration:options:animations:completion
My implementation inside the container view:
[UIView transitionWithView:self
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromBottom
animations: ^{
[self.backView removeFromSuperview];
[self addSubview:self.frontView];
}
completion:NULL];
The trick was the UIViewAnimationOptionTransitionFlipFromBottom option. Incidentally, Apple has this exact bit of code in their documentation. You can also add other animations to the block like resizing and moving.
Ok, this won't be a complete solution but I'll point out some things that might be helpful. I'm not a Core-Animation guru but I have done a few 3D rotations in my program.
First, there is no 'back' to a view. So if you rotate something by M_PI (180 degrees) you're going to be looking at that view as though from the back (which is why it's upside down/mirrored).
I'm not sure what you mean by:
Only half of the card across the x-axis is animated.
But, it it might help to consider your anchor point (the point at which the rotation occurs). It's usually in the center, but often you need it to be otherwise. Note that anchor points are expressed as a proportion (percentage / 100)...so the values are 0 - 1.0f. You only need to set it once (unless you need it to change). Here's how you access the anchor point:
layer.anchorPoint = CGPointMake(0.5f, 0.5f) //This is center
The reason the animation only ever runs once is because transforms are absolute, not cumulative. Consider that you're always starting with the identity transform and then modifying that, and it'll make sense...but basically, no animation occurs because there's nothing to animate the second time (the view is already in the state you're requesting it to be in).
If you're animating from one view to another (and you can't use [UIView transitionWithView:duration:options:animations:completion:];) you'l have to use a two-stage animation. In the first stage of the animation, for the 'card' that is being flipped to backside, you'll rotate the view-to-disappear 'up/down/whatever' to M_PI_2 (at which point it will be 'gone', or not visible, because of it's rotation). And in the second stage, you're rotate the backside-of-view-to-disappear to 0 (which should be the identity transform...aka, the view's normal state). In addition, you'll have to do the exact opposite for the 'card' that is appearing (to frontside). You can do this by implementing another [UIView animateWithDuration:...] in the completion block of the first one. I'll warn you though, doing this can get a little bit complicated. Especially since you're wanting views to have a 'backside', which will basically require animating 4 views (the view-to-disappear, the view-to-appear, backside-of-view-to-disappear, and the backside-of-view-to-appear). Finally, in the completion block of the second animation you can do some cleanup (reset view that are rotated and make their alpha 0.0f, etc...).
I know this is complicated, so you might want read some tutorial on Core-Animation.
#Aaron has some good info that you should read.
The simplest solution is to use a CATransformLayer that will allow you to place other CALayer's inside and maintain their 3D hierarchy.
For example to create a "Card" that has a front and back you could do something like this:
CATransformLayer *cardContainer = [CATransformLayer layer];
cardContainer.frame = // some frame;
CALayer *cardFront = [CALayer layer];
cardFront.frame = cardContainer.bounds;
cardFront.zPosition = 2; // Higher than the zPosition of the back of the card
cardFront.contents = (id)[UIImage imageNamed:#"cardFront"].CGImage;
[cardContainer addSublayer:cardFront];
CALayer *cardBack = [CALayer layer];
cardBack.frame = cardContainer.bounds;
cardBack.zPosition = 1;
cardBack.contents = (id)[UIImage imageNamed:#"cardBack"].CGImage; // You may need to mirror this image
[cardContainer addSublayer:cardBack];
With this you can now apply your transform to cardContainer and have a flipping card.
#Paul.s
I followed your approach with card container but when i applt the rotation animation on card container only one half of the first card rotates around itself and finally the whole view appears.Each time one side is missing in the animation
Based on Paul.s this is updated for Swift 3 and will flip a card diagonally:
func createLayers(){
transformationLayer = CATransformLayer(layer: CALayer())
transformationLayer.frame = CGRect(x: 15, y: 100, width: view.frame.width - 30, height: view.frame.width - 30)
let black = CALayer()
black.zPosition = 2
black.frame = transformationLayer.bounds
black.backgroundColor = UIColor.black.cgColor
transformationLayer.addSublayer(black)
let blue = CALayer()
blue.frame = transformationLayer.bounds
blue.zPosition = 1
blue.backgroundColor = UIColor.blue.cgColor
transformationLayer.addSublayer(blue)
let tgr = UITapGestureRecognizer(target: self, action: #selector(recTap))
view.addGestureRecognizer(tgr)
view.layer.addSublayer(transformationLayer)
}
Animate a full 360 but since the layers have different zPositions the different 'sides' of the layers will show
func recTap(){
let animation = CABasicAnimation(keyPath: "transform")
animation.delegate = self
animation.duration = 2.0
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.toValue = NSValue(caTransform3D: CATransform3DMakeRotation(CGFloat(Float.pi), 1, -1, 0))
transformationLayer.add(animation, forKey: "arbitrarykey")
}