Move image 90 degree using Bezier curve - ios

I am new to iOS please some one help me to move image using Bezier curve along 90 degree path. I had searched but my doubt is not clear yet, So please help me.
I had tried this code but I would like to use Bezier curve.
- (void)startApp {
UIImageView *imageToMove =
[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"man.jpg"]];
imageToMove.frame = CGRectMake(10, 10, 100, 100);
[self.view addSubview:imageToMove];
// Move the image
[self moveImage:imageToMove
duration:1.0
curve:UIViewAnimationCurveLinear
x:70.0
y:70.0];
}
- (void)moveImage:(UIImageView *)image
duration:(NSTimeInterval)duration
curve:(int)curve
x:(CGFloat)x
y:(CGFloat)y {
// Setup the animation
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:duration];
[UIView setAnimationCurve:curve];
[UIView setAnimationBeginsFromCurrentState:YES];
// The transform matrix
CGAffineTransform transform = CGAffineTransformMakeTranslation(x, y);
image.transform = transform;
// Commit the changes
[UIView commitAnimations];
}

You can rotate (transform) view using CABasicAnimation also. I have done some calculation for that.
- (void) rotateAnimation {
CABasicAnimation *topAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
topAnimation.autoreverses = NO;
topAnimation.duration = 0.8;
topAnimation.repeatDuration = 0;
topAnimation.toValue = [NSValue valueWithCATransform3D:CATransform3DPerspect(CATransform3DMakeRotation(M_PI, 1, 0, 0), CGPointMake(0, 0), 400)];
[image.layer addAnimation:topAnimation forKey:nil];
}
And supporting methods for that rotation are below:
CATransform3D CATransform3DMakePerspective(CGPoint center, float disZ)
{
CATransform3D transToCenter = CATransform3DMakeTranslation(-center.x, -center.y, 0);
CATransform3D transBack = CATransform3DMakeTranslation(center.x, center.y, 0);
CATransform3D scale = CATransform3DIdentity;
scale.m34 = -1.0f/disZ;
return CATransform3DConcat(CATransform3DConcat(transToCenter, scale), transBack);
}
CATransform3D CATransform3DPerspect(CATransform3D t, CGPoint center, float disZ)
{
return CATransform3DConcat(t, CATransform3DMakePerspective(center, disZ));
}
About rotation is on X-axis (horizontal) on respective layer. You can change axis value to Y and Z from below line in rotateAnimation.
CATransform3DMakeRotation(M_PI, 1, 0, 0), CGPointMake(0, 0), 400)]
By changing 1, 0, 0 values to 0, 1, 0 in above code will change rotation on Y axis and similarly for Z axis. Replace values and have look for rotation you like.

Here is my UIImageView with IBOutlet named drawPad.
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150)
radius:100
startAngle:0
endAngle:(22.0f/7.0f)
clockwise:YES];
UIGraphicsBeginImageContext(self.view.frame.size);
path.lineCapStyle = kCGLineCapRound;
path.lineWidth = 10.0f;
[[UIColor blackColor] setStroke];
[path stroke];
self.drawpad.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Related

Why CALayer and CAShapeLayer behaving differently

First I created the CAShapeLayer for masking the UIView at the bottom with height of 10px like this:
CAShapeLayer *mask = [[CAShapeLayer alloc]init];
CGRect maskRect = CGRectMake(0, self.view.frame.size.height-10, self.view.frame.size.width, 10.0);
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
mask.path = path;
CGPathRelease(path);
mask.anchorPoint = CGPointMake(0.5, 1.0);
self.view.layer.mask = mask;
After commenting the above code I created a CALayer for masking the UIView at the bottom with height of 10px like this:
CALayer *mask = [CALayer layer];
mask.contents = (id)[[self getImg]CGImage];
mask.frame = CGRectMake(0, self.view.frame.size.height-5, self.view.frame.size.width, 10.0);
mask.anchorPoint = CGPointMake(0.5, 1.0);
self.view.layer.mask = mask;
Question 1:
To perfectly touch the bottom not a pixel above or below, for CAShapeLayer the CGRect I defined is
CGRectMake(0, self.view.frame.size.height-10, self.view.frame.size.width, 10.0);
and for CALayer the CGRect I defined is
CGRectMake(0, self.view.frame.size.height-5, self.view.frame.size.width, 10.0);
why -10 for CAShapeLayer and why -5 for CALayer ?
The animation code and its effects on CAShapeLayer and CALayer:
CGRect oldBounds = mask.bounds;
CGRect newBounds = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height/2);
[UIView animateWithDuration:1.0 animations:^{
CABasicAnimation* revealAnimation = [CABasicAnimation animationWithKeyPath:#"bounds"];
revealAnimation.fromValue = [NSValue valueWithCGRect:oldBounds];
revealAnimation.toValue = [NSValue valueWithCGRect:newBounds];
revealAnimation.duration = 0.5;
[mask addAnimation:revealAnimation forKey:#"revealAnimation"];
}];
// Update the bounds so the layer doesn't snap back when the animation completes.
mask.bounds = newBounds;
Effect on CAShapeLayer the position is changed but no effect on hight like this:
Effect on CALayer which worked perfectly and the result I wanted:
Question 2:
Why CAShapeLayer changed the position not the height?
I don't know why but I am having a feeling like CALayer animation is not going smooth?
Question 3:
The [UIView animateWithDuration:1.0 animations:^{ ... block do not have any effect on animation, why?
Answer 1:
Not sure why it is 5 and 10 for CAShapeLayer and CALayer, need to debug closely, but maybe problem is in adjusting anchorPoint or because you use path of shape layer
Answer 2:
About effect on CAShapeLayer: this is because you assigned CGPath to it, and changed shape layer frame, but not path. So you need to set new path property with correct coordinates. Something like this:
CGRect maskRect = CGRect newBounds = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height/2);
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
mask.path = path;
CGPathRelease(path);
Answer 3:
CABasicAnimation is class, which will do all animation, and you don't need to use it in UIView animateWithDuration block.
This code should work as expected:
CABasicAnimation* revealAnimation = [CABasicAnimation animationWithKeyPath:#"bounds"];
revealAnimation.fromValue = [NSValue valueWithCGRect:oldBounds];
revealAnimation.toValue = [NSValue valueWithCGRect:newBounds];
revealAnimation.duration = 0.5;
[mask addAnimation:revealAnimation forKey:#"revealAnimation"];

How to create Custom flip animation using UIView

I am trying to create a custom animation something like the default flip animation for a UIView, but without the actual flip, and also the axis of the flip I want to be on the left side not on the center. Here is an example of what I am trying to accomplish but without the repeat:
Also in the code I will add UILabel as sub view and a few more elements.
If you can provide a code example it will be much appreciated
Thank you very much in advance for any support!
Update:
used the example provided by #artal and obtained the wanted effect for UIImageView but how can I obtain the same example for an UIButton?
Tried the next code:
objectivesButton = [[UIButton alloc] initWithFrame:CGRectMake( 0.25f * [UIScreen mainScreen].bounds.size.width, 0.7f * [UIScreen mainScreen].bounds.size.height, 0.5f * [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height * 0.3f)];
[objectivesButton addTarget:self
action:#selector(startAnimationButton)
forControlEvents:UIControlEventTouchUpInside];
objectivesButton.backgroundColor = [UIColor yellowColor];
objectivesButton.layer.anchorPoint = CGPointMake(0, 0.5);
[self.view addSubview:objectivesButton];
- (void)startAnimationButton{
CATransform3D perspectiveTrasnform = CATransform3DIdentity;
perspectiveTrasnform.m34 = -0.004;
CGFloat rotateAngleDeg = 90;
CATransform3D flipTransform = CATransform3DRotate(perspectiveTrasnform, rotateAngleDeg / 180.0 * M_PI, 0, 1.5, 0);
[UIView animateWithDuration:3.0f animations:^()
{
objectivesButton.layer.transform = flipTransform;
}];
}
You can animate the view this way by applying a perspective and rotation transform to its underlying CALayer (a 3D transform).
To start it from the left side you can set the anchorPoint of the layer.
Here's an example:
UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"image.png"]];
imageView.center = CGPointMake(self.view.center.x - imageView.frame.size.width * 0.5, self.view.center.y + imageView.frame.size.height * 0.5);
imageView.layer.anchorPoint = CGPointMake(0, 1);
[self.view addSubview:imageView];
CATransform3D perspectiveTrasnform = CATransform3DIdentity;
perspectiveTrasnform.m34 = -0.004;
CGFloat rotateAngleDeg = 90;
CATransform3D flipTransform = CATransform3DRotate(perspectiveTrasnform, rotateAngleDeg / 180.0 * M_PI, 0, 1, 0);
[UIView animateWithDuration:5 animations:^()
{
imageView.layer.transform = flipTransform;
}];

Circular animation

I want to implement an animation, its like from a point view starts expanding circularly and filling the entire view. I have implemented the following code but it does not fill the view circularly. Any ideas?
- (void)viewDidLoad {
[super viewDidLoad];
myView =[[UIView alloc]init];
myView.frame=CGRectMake(self.view.frame.size.width/2,self.view.frame.size.height/2+100, .1, .1);
myView.backgroundColor=[UIColor redColor];
[self.view addSubview:myView];
[self animate];
}
-(void)animate{
[UIView animateWithDuration:1.0
delay: 0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[self setRoundedView: myView toDiameter:1000];
}
completion:nil];
}
-(void)setRoundedView:(UIView *)roundedView toDiameter:(float)newSize;
{
CGPoint saveCenter = roundedView.center;
CGRect newFrame = CGRectMake(roundedView.frame.origin.x, roundedView.frame.origin.y, newSize, newSize);
roundedView.frame = newFrame;
roundedView.layer.cornerRadius = newSize / 2.0;
roundedView.center = saveCenter;
}
You can create a CAShape layer with circular Bezier Path and animate the path property.
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath: #"path"];
Check here for more details : Animating CAShapeLayer size change
If you dont want to use CAnimation you can just scale your rounded view like this :
[UIView animateWithDuration:1.0
delay: 0.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
myView.transform = CGAffineTransformMakeScale(scaleX,scaleY);
}
completion:nil];
Try this code. May be helpful.
// Set up the shape of the circle
int radius = 100;
CAShapeLayer *circle = [CAShapeLayer layer];
// Make a circular shape
circle.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius)
cornerRadius:radius].CGPath;
// Center the shape in self.view
circle.position = CGPointMake(CGRectGetMidX(self.view.frame)-radius,
CGRectGetMidY(self.view.frame)-radius);
// Configure the apperence of the circle
circle.fillColor = [UIColor redColor].CGColor;
circle.strokeColor = [UIColor blackColor].CGColor;
circle.lineWidth = 5;
// Add to parent layer
[self.view.layer addSublayer:circle];
// Configure animation
CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
drawAnimation.duration = 10.0; // "animate over 10 seconds or so.."
drawAnimation.repeatCount = 1.0; // Animate only once..
// Animate from no part of the stroke being drawn to the entire stroke being drawn
drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
drawAnimation.toValue = [NSNumber numberWithFloat:1.0f];
// Experiment with timing to get the appearence to look the way you want
drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// Add the animation to the circle
[circle addAnimation:drawAnimation forKey:#"drawCircleAnimation"];

How to put 2 layers to follow same bezierpath without one hiding the other

I have created 2 CALayers of same size and am passing these to the method below. However, the two layers runs together. How can I seperate these so that both are visible?
- (void) myAnimation : (CALayer *) sublayer {
UIBezierPath* aPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(30, 100, 270, 270)];
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = aPath.CGPath;
anim.rotationMode = kCAAnimationRotateAuto;
anim.repeatCount = HUGE_VALF;
anim.duration =35.0;
[sublayer addAnimation:anim forKey:#"race"];
}
Your path begins and ends at the same point. Assuming both beginning times are the same and duration is the same, your layers will precisely overlap. You can either shift the beginning time or rotate your UIBezierPath* aPath Here's an example of rotating your UIBezierPath* aPath and altering the duration. It should give you an idea how you could alter duration, begin time, rotation, etc.
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor blackColor]];
CALayer * layer1 = [CALayer layer];
CALayer * layer2 = [CALayer layer];
[layer1 setFrame:CGRectMake(0, 0, 5, 50)];
[layer2 setFrame:CGRectMake(0, 0, 5, 100)];
layer1.backgroundColor = [[UIColor colorWithRed:.1 green:.5 blue:1 alpha:.35] CGColor];
layer2.backgroundColor = [[UIColor colorWithRed:.9 green:.2 blue:.5 alpha:.35] CGColor];
[self.view.layer addSublayer:layer1];
[self.view.layer addSublayer:layer2];
[self myAnimation:layer1 andRotation:0 andDuration:35.0];
[self myAnimation:layer2 andRotation:10 andDuration:10.0];
}
- (void) myAnimation:(CALayer *)sublayer andRotation:(CGFloat)rot andDuration:(CFTimeInterval)dur {
CGRect rect = CGRectMake(30, 100, 270, 270);
UIBezierPath* aPath = [UIBezierPath bezierPathWithOvalInRect:rect];
// Creating a center point around which we will transform the path
CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
CGAffineTransform transform = CGAffineTransformIdentity;
transform = CGAffineTransformTranslate(transform, center.x, center.y);
transform = CGAffineTransformRotate(transform, rot);
// Recenter the transform
transform = CGAffineTransformTranslate(transform, -center.x, -center.y);
// This is the new path we will use.
CGPathRef rotated = CGPathCreateCopyByTransformingPath(aPath.CGPath, &transform);
CAKeyframeAnimation *anim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
anim.path = rotated;
anim.rotationMode = kCAAnimationRotateAuto;
anim.repeatCount = HUGE_VALF;
anim.duration =dur;
[sublayer addAnimation:anim forKey:#"race"];
}

Rotating UIImageView more than 180 degrees using animations on iOS

Im trying to animate a moving meter by rotating it more than 180 degrees, but it wont work. Im using CGAffineTransform to rotate the meter, which uses a net result to make the rotation. This means that i cannot choose CW or CCW rotation when using this function. How can I modified this code to make it rotate 4 radians. Currently the rotation is transformed to a net result, making the rotation minimal.
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:1.25];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
CGAffineTransform transform = CGAffineTransformMakeRotation(4);
self.meter.transform = transform;
[UIView commitAnimations];
EDIT:
From the answers i managed to get it working by doing this
[UIView animateWithDuration:1.5 animations:^{
CABasicAnimation *fullRotation;
fullRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:((330*M_PI)/180)];
fullRotation.duration = 2.0f;
fullRotation.repeatCount = 1;
fullRotation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
fullRotation.fillMode = kCAFillModeForwards;
fullRotation.removedOnCompletion = NO;
// add animation in your view
[self.meter.layer addAnimation:fullRotation forKey:#"330"];
[self performSelector:#selector(stopRotate:) withObject:self.meter afterDelay:2.0];
}];
//Add This in header prefix
#define MAXFLOAT 0x1.fffffep+127f
// add For rotation
[UIView animateWithDuration:1.5 animations:^{
CABasicAnimation *fullRotation;
fullRotation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
fullRotation.fromValue = [NSNumber numberWithFloat:0];
fullRotation.toValue = [NSNumber numberWithFloat:(2*M_PI))];
fullRotation.duration =1.0f;
fullRotation.repeatCount = MAXFLOAT;
// add animation in your view
[yourView.layer addAnimation:fullRotation forKey:#"360"];
[self performSelector:#selector(stopRotate:) withObject:yourView afterDelay:1.0];
}];
And Add this code to stop rotation From view
-(void)stopRotate :(UIView *)yourView
{
[yourView.layer removeAnimationForKey:#"360"];
}
Try this, just change the basic properties to match your requirements:
CATransform3D rotationTransform = CATransform3DMakeRotation(((4) * (180.0 / M_PI)), 0, 0, 1.0);
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
rotationAnimation.toValue = [NSValue valueWithCATransform3D:rotationTransform];
rotationAnimation.duration = 0.6f;
rotationAnimation.cumulative = YES;
rotationAnimation.repeatCount = 1;
//Add this two lines
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.removedOnCompletion = NO;
[self.meter.layer addAnimation:rotationAnimation forKey:#"rotationAnimation"];
Let me know is it works for you.
You need to convert the radian to degree using the following macro:
#define RADIANS_TO_DEGREES(radians) ((radians) * (180.0 / M_PI))
and pass the value to the function:
-(UIImage*)rotateImage:(UIImage*)img forAngle:(CGFloat)degree {
UIView *rotatedViewBox = [[UIView alloc]initWithFrame:CGRectMake(0, 0, img.size.width, img.size.height)];
CGAffineTransform t = CGAffineTransformMakeRotation(degree);
rotatedViewBox.transform = t;
CGSize rotatedSize = rotatedViewBox.frame.size;
UIGraphicsBeginImageContext(rotatedSize);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextTranslateCTM(context, rotatedSize.width, rotatedSize.height);
CGContextRotateCTM(context, radian);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextDrawImage(context, CGRectMake(-img.size.width, -img.size.height, img.size.width, img.size.height), img.CGImage);
UIImage *returnImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return returnImg;
}

Resources