I am using a CircleView class that basically inherits off UIView and implements drawRect to draw a circle. This all works, hurrah!
What I cannot figure out though is how to make it so when I touch it (touch code is implemented) the circle grows or pops. Normally I'd use the UIKit animation framework to do this but given I am basically overriding the drawRect function to directly draw the circle. So how do I animate this?
- (void)drawRect:(CGRect)rect{
CGContextRef context= UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, _Color.CGColor);
CGContextFillEllipseInRect(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height));
}
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer {
// Animate?
}
The answers depends on what you mean by "grows or pops". When I hear "pop" I assume that the view scales up over a short period of time ans scales back down again to the original size. Something that "grows" on the other hand would scale up but not down again.
For something that scales up and down again over a short period of time I would use a transform to scale it. Custom drawing or not, UIView has build in support for animating a simple transform. If this is what you are looking for then it's not more then a few lines of code.
[UIView animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionAutoreverse // reverse back to original value
animations:^{
// scale up 10%
yourCircleView.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
// restore the non-scaled state
yourCircleView.transform = CGAffineTransformIdentity;
}];
If on the other hand you want the circle to grow a little bit more on every tap then this won't do it for you since the view is going to look pixelated when it scales up. Making custom animations can be tricky so I would still advice you to use a scaling transform for the actual animation and then redraw the view after the animation.
[UIView animateWithDuration:0.3
animations:^{
// scale up 10%
yourCircleView.transform = CGAffineTransformMakeScale(1.1, 1.1);
} completion:^(BOOL finished) {
// restore the non-scaled state
yourCircleView.transform = CGAffineTransformIdentity;
// redraw with new value
yourCircleView.radius = theBiggerRadius;
}];
If you really, really want to do a completely custom animation then I would recommend that you watch Rob Napiers talk on Animating Custom Layer Properties, his example is even the exact thing you are doing (growing a circle).
If you want an animation that expands the ellipse from the centre, try this. In the header, define 3 variables:
BOOL _isAnimating;
float _time;
CGRect _ellipseFrame;
Then implement these 3 methods in the .m file:
- (void)drawRect:(CGRect)rect; {
[super drawRect:rect];
CGContextRef context= UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, _Color.CGColor);
CGContextFillEllipseInRect(context, _ellipseFrame);
}
- (void)expandOutward; {
if(_isAnimating){
_time += 1.0f / 30.0f;
if(_time >= 1.0f){
_ellipseFrame = self.frame;
_isAnimating = NO;
}
else{
_ellipseFrame = CGRectMake(0.0f, 0.0f, self.frame.size.width * _time, self.frame.size.height * _time);
_ellipseFrame.center = CGPointMake(self.frame.size.width / 2.0f, self.frame.size.height / 2.0f);
[self setNeedsDisplay];
[self performSelector:#selector(expandOutward) withObject:nil afterDelay:(1.0f / 30.0f)];
}
}
}
- (void)handleSingleTap:(UITapGestureRecognizer *)recognizer; {
if(_isAnimating == NO){
_time = 0.0f;
_isAnimating = YES;
[self expandOutward];
}
}
This is the most basic way you can animate the circle expanding from the centre. Look into CADisplayLink for a constant sync to the screen if you want more detailed animations. Hope that Helps!
Related
I want to apply an animation to two UIImageViews. The idea is for image one to flip horizontally by 90 degrees and then image two to complete the rotation. Think of it as a coin spinning: Head side (image one) faces forward -> turn 90 degrees -> Tail side (image two) rotates to face forward. I can do the first half of the animation but I am stuck on the second.
[UIView animateWithDuration:1.0f animations:^{
image1.transform = CGAffineTransformMakeScale(-0.01, 1);
} completion: ^(BOOL finished) {
// How can I make image 2 to rotate out as if it initially was already rotated by 90 degrees?
}];
for flipping animation, there a animation option called UIViewAnimationOptionTransitionFlipFromRight, use it with the UIView's animation method for example, like below for example
[UIView transitionWithView:myImageView //with first image
duration:animationDuration
options:UIViewAnimationOptionTransitionFlipFromRight
animations:^{
myImageView.image = toImage; //to next image
} completion:^(BOOL finished) {
//completion actions after flipped
}];
there are other animation options also there, like FromLeft,
FromTop,FromBottom use any one to your requirement
Take a look at this question about flipping a UIView in 3D using CATransform3D.
Simple Core Animations 3D transform of UIView
The answer:
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / -1000.0;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, M_PI * 0.6, 1.0f, 0.0f, 0.0f);
[UIView animateWithDuration:1.0 animations:^{
self.someView.layer.anchorPoint = CGPointMake(0.5, 0);
self.someView.layer.transform = rotationAndPerspectiveTransform;
} completion:^(BOOL finished){
// code to be executed when flip is completed
}];
I am making a card game and would like to use a Card flipping animation.
Currently I'm using this code with two images, front and back:
card, is a uiview with two UIImageView propertys
[UIView transitionWithView:card duration:0.65f
options:UIViewAnimationOptionTransitionFlipFromRight animations:^{
card.backImageView.image = card.frontImageView.image;
card.layer.shadowOpacity = 0.8;
} completion:^(BOOL finished){
card.layer.shadowOpacity = 0.0;
}];
In order to create a basic card flipping animation like the one in the video you've linked to, I suggest putting frontImageView and the backImageView directly on top of each other on the UIView you intend to flip. To start, set their images to front and back accordingly; and, in this particular case, hide the frontImageView and show the backImageView.
Assuming "card" is the UIView you intend to flip, to perform the flip try:
[UIView transitionWithView:card duration:0.65f
options:UIViewAnimationOptionTransitionFlipFromRight animations:^{
frontImageView.hidden = NO;
backImageView.hidden = YES;
} completion:^(BOOL finished) {
// whatever you'd like to do immediately after the flip completes
}];
Edit:
And to handle the shadow, first off, it appears in the video you posted that the shadow grows in length moreso than it just fades in. And it seems as if (and makes logical sense that) the shadow reaches its peak during the middle of the animation as the card is lifted at its highest point. Since the shadow grows then shrinks during the course of the flip animation, it doesn't make sense to include the shadow animation within the same animation block as the flip since they're on different time schedules.
Secondly with regard to the shadow, to animate the layer property, you have to use Core Animations.
Perhaps you can run the two animations concurrently, i.e. while the above animation is performing, also do something like:
CABasicAnimation *shadowAnimation = [CABasicAnimation animationWithKeyPath:#"shadowRadius"];
shadowAnimation.delegate = self;
[shadowAnimation setFromValue:[NSNumber numberWithFloat:3.0]];
[shadowAnimation setToValue:[NSNumber numberWithFloat:10.0]];
[shadowAnimation setDuration:0.65f];
shadowAnimation.autoreverses = YES;
[[card layer] addAnimation:shadowAnimation forKey:#"shadowRadius"];
The last portion has been adapted from this code and takes advantage of the autoreverse property to automatically reverse the shadow's growth.
This is how to perform a card flipping animation in swift 3:
UIView.transition(from: frontImageView,
to: backImageView,
duration: 0.65,
options: .transitionFlipFromRight,
completion: nil)
i tested a few things, and Compromise with this:
_tempTransform = card.transform;
[card loadFront];//loads the front image.
card.frontImageView.hidden=YES;
[UIView animateWithDuration:0.02f
delay:0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
CATransform3D transform = CATransform3DIdentity;
transform.m34 = 1.0 / -500;
transform = CATransform3DRotate(transform, 15.0f * M_PI / 180.0f, 0, -1, 0.0f);
card.layer.transform = transform;
}completion:^(BOOL done){
card.layer.shadowOpacity=0.1f;
card.transform=CGAffineTransformScale(card.transform, 1.2f, 1.2f);
[UIView transitionWithView:card duration:0.4f
options:UIViewAnimationOptionTransitionFlipFromRight animations:^{
card.frontImageView.hidden=NO;
card.backImageView.hidden=YES;
} completion:^(BOOL finished){
card.layer.shadowOpacity=0.0f;
card.transform=_tempTransform;
}];
}];
I am drawing a bar chart, it works fine except it dosent have animation. Ex: fill colore 0-50%.
I am using simple DrawRect method to draw here is my code:
- (void)drawRect:(CGRect)rect
{
CGFloat height = self.bounds.size.height;
CGContextRef context = UIGraphicsGetCurrentContext();
// CGContextClearRect(context, rect);
CGContextSetFillColorWithColor(context, [self colorWithR:149 G:21 B:29 A:1].CGColor);
CGFloat barWidth = 52;
int count = 0;
NSNumber *num = [NSNumber numberWithFloat:graphValue];
CGFloat x = count * (barWidth + 10);
CGRect barRect = CGRectMake(x, height - ([num floatValue] * height), barWidth, [num floatValue] * height);
CGContextAddRect(context, barRect);
count++;
CGContextFillPath(context);
}
Please help me know the simple way to add animation.
I believe you want to animate the heights of the bars in the bargraph.
I suggest you implement each bar as a separate UIview, a simple rectangle. You can also put all of them in one view with a custom drawRect. Then you need to scale these views or change their frame inside the animation block of either of the following methods:
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion
OR
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
For a great tutorial see this.
This is an example if you don't have a lot of time.
[UIView animateWithDuration:1//Amount of time the animation takes.
delay:0//Amount of time after which animation starts.
options: UIViewAnimationCurveEaseOut//How the animation will behave.
animations:^{
//here you can either set a CGAffineTransform, or change your view's frame.
//Both will work just fine.
yourBarView.transform = CGAffineTransformMakeScale (
scaleForX,//say 1, you dont want it to change.
scaleForY//say 20, you want to make it 20 times larger in the y
//direction.
//Note* to animate from a zero height to a finite height change the
//view's frame.
//yourBarView.frame = CGRectMake(0,0,20,100);
);
}
completion:^(BOOL finished){//This block is called when the animation completes.
NSLog(#"Done!");
}];
So I have subclassed UIView and added some drawing code. I am scaling the resulting view up and down.
I would like this view to be resolution independent so that it is legible at any size, and I won't need to manage multiple images etc. etc.
As a test I made up a bit of drawing code that looks like this.
It creates concentric ovals that fit within whatever frame size the UIView has.
Actually the outside ring is a little smaller than the frame so it isn't clipped. Fine for this. The actual graphic will be more complex and will contain text which must be readable at small sizes and things of that nature.
- (void)drawRect:(CGRect)rect
{
UIColor* color = [UIColor colorWithRed: 0.833 green: 0.833 blue: 0.833 alpha: 1];
float width = self.bounds.size.width;
float height = self.bounds.size.height;
float scalePercent = 0.8;
for(int i = 0; i < 10; i++){
width = width * scalePercent;
height = height * scalePercent;
float x = (self.bounds.size.width - width) / 2;
float y = (self.bounds.size.height - height) / 2;
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(x, y, width, height)];
[color setStroke];
ovalPath.lineWidth = 2;
[ovalPath stroke];
}
}
Now here's the scaling:
- (void) makeBig{
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:(void (^)(void)) ^{
self.transform = CGAffineTransformMakeScale(2, 2);
}
completion:^(BOOL finished){
}];
}
When you run this the view zooms up, but it is pixelated. It's pixelated because the view has doubled in size but it's resolution has not changed.
So, here's how not to do it.
- (void) makeBig{
[UIView animateWithDuration:0.5
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:(void (^)(void)) ^{
self.transform = CGAffineTransformMakeScale(2, 2);
}
completion:^(BOOL finished){
CGRect targetFrame = self.frame;
self.transform = CGAffineTransformIdentity;
self.frame = targetFrame;
[self setNeedsDisplay];
}];
}
This works, but the fix is visible at the end of the animation when the resolution snaps back to screen resolution.
I could try pre-scaling the view up and pre-drawing at the final size then scaling it down and then running the animation to scale it back up again, but for various reasons that I can think of that sounds totally stupid. I suppose I could be wrong and it's the brightest idea since fire. I kind of doubt it though.
So how is a smooth scale of vector content done best?
View-based animation is really handy, but if I'm not mistaken it uses CABasicAnimation, which only uses a single keyframe. It'll be a little more code, but if you use a CAKeyframeAnimation instead, Core Animation will redraw the contents of the animated layer for each keyframe (you get to specify when those occur), so you can avoid the appearance of pixelation.
Can me explane how I can rotate image use anchor point like
https://www.dropbox.com/s/vh3h5cr1mkdbfh3/ex_image2.JPG
on .m
#import <QuartzCore/QuartzCore.h>
on .h
[UIView animateWithDuration:0.7f
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^
{
self.center = position;
self.layer.anchorPoint = CGPointMake(-1, 0);
self.transform = CGAffineTransformMakeRotation(-5);
}
completion:^(BOOL completed){
}];
When I use this code I have something like that
https://www.dropbox.com/s/v87abux9dqm4y0p/ex_image1.JPG
When you apply a rotation transform to a layer, the rotation occurs around the anchor point.
So set the layer's anchor point to (0.0, 0.0) first.
self.layer.anchorPoint = CGPointMake(0.0f, 0.0f);
Then you can just rotate the view and the rotation will occur around the anchor point, (0.0, 0.0).
[UIView animateWithDuration:2.0f animations:^{
self.transform = CGAffineTransformMakeRotation(3.1415f);
}];
The code will rotate the view one whole time in the way you have pictured.
You can check out the section titled "Anchor Points Affect Geometric Manipulations" in Apple's "Core Animation Programming Guide" for more information.