In my iPhone app i need to implement a different type of transition.
that was
next view open from the current view,
it stats like a dot, and the dot expands slowly like a circle with in the circle next view is to be displayed partially with in the part of the circle , finally the circle expands totally the next view appears completely.
I search lots of transitions like CATransitions, and some animations on cocoa controller, but i didn't find this type of transition, can any one help me please.
Well I think I can offer you a workaround. Instead of pushing to the next view like a dot . I suggest you add a simple dot animation in the ViewWillAppear of the view in which you have to get pushed. Now the push method would remain the same like
[self.navigationController pushViewController:NewView animated:YES];
But in the ViewWillAppear the code would be such that the dot would expand to a circle and reveal the New View beneath it. Hope you understand the logic I am trying to explain here. Any issue do let me know .
in my case I did it that way:
set a CAShapeLayer instance as the layer's mask property of your custom view subclass
#interface MyCustomView ()
#property (nonatomic, strong) CircleShapeLayer *circleShapeLayer;
#end
#implementation MyCustomView
- (id) initWithFrame: (CGRect) frame {
self = [super initWithFrame: CGRectZero];
if (self) {
self.layer.mask = self.shapeLayer;
[self.layer.mask setValue: #(0) forKeyPath: #"transform.scale"];
}
return self;
}
zoom this mask layer to fullsize. code of your view:
- (void) zoom {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: #"transform.scale"];
animation.fromValue = [self.layer.mask valueForKeyPath: #"transform.scale"];
animation.toValue = #(1);
animation.duration = 2.0;
animation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut];
animation.delegate = self;
// Important: change the actual layer property before installing the animation.
[self.layer.mask setValue: animation.toValue forKeyPath: animation.keyPath];
// Now install the explicit animation, overriding the implicit animation.
[self.layer.mask addAnimation: animation forKey: animation.keyPath];
return;
}
- (CAShapeLayer *) circleShapeLayer {
if (!_ circleShapeLayer) {
_circleShapeLayer = [SGMaskLayer layer];
_circleShapeLayer.delegate = _shapeLayer;
_circleShapeLayer.frame = self.bounds;
_circleShapeLayer.needsDisplayOnBoundsChange = YES;
}
return _circleShapeLayer;
}
#end
the code of the mask layer:
#interface CircleShapeLayer : CAShapeLayer
#end
#implementation CircleShapeLayer
- (void) drawLayer: (CALayer *) layer inContext: (CGContextRef) ctx {
UIGraphicsPushContext(ctx);
UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect: self.bounds];
self.path = circlePath.CGPath;
UIGraphicsPopContext();
}
#end
from the documentation:
The layer’s alpha channel determines how much of the layer’s content
and background shows through. Fully or partially opaque pixels allow
the underlying content to show through but fully transparent pixels
block that content.
The default value of this property is nil nil. When configuring a
mask, remember to set the size and position of the mask layer to
ensure it is aligned properly with the layer it masks.
so I drew a circle with UIBezierPath to achieve the round mask. at the beginning I set the mask's scale factor to 0 so nothing of the view's layer is visible. then the scale factor is set to 1 (filling the layer's bounds) animated which gives a nice animation of a circle increasing it's radius from the center.
you might need one more animation shifting the center point of your view. both animations can be wrapped in a CAAnimationGroup.
Open in first view:
//Delegate method for annotation did select
- (void)tapOnAnnotation:(RMAnnotation *)annotation onMap:(RMMapView *)map;
{
//To get the touch point of the GMS marker to set them as circle transiton pos
CGPoint markerPoint = annotation.position;
x = markerPoint.x;
y = markerPoint.y;
circleSize = 10;
radiusChange = 0;
//Populate Same Values to next view to close
VenueScreen.x = x;
VenueScreen.y = y;
VenueScreen.view.hidden = YES;
timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(openVenueScreen) userInfo:nil repeats:YES];
VenueScreen.view.frame = CGRectMake(0, 0, 320, 480);
[self.view addSubview:VenueScreen.view];
}
//Circular transition to open Next view
-(void)openVenueScreen
{
VenueScreen.view.hidden = NO;
VenueScreen.view.userInteractionEnabled = NO;
VenueScreen.view.alpha = 0.9;
self.view.userInteractionEnabled = NO;
int rChange = 0; // Its doing proper masking while changing this value
int radius = circleSize-rChange;
CAShapeLayer *circleShapeLayer = [CAShapeLayer layer];
// Make a circular shape
circleShapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(x+radiusChange, y+radiusChange, 2.0*radius, 2.0*radius)
cornerRadius:radius].CGPath;
radiusChange = radiusChange-10;
// Configure the apperence of the circle
[[VenueScreen.view layer] setMask:circleShapeLayer];
//Start Transition
circleSize = circleSize+10;
if (circleSize > 480)
{
[[VenueScreen.view layer] setMask:nil];
[timer invalidate]; //Stop titmer
VenueScreen.view.userInteractionEnabled = YES;
self.view.userInteractionEnabled = YES;
VenueScreen.view.alpha = 1;
//Populate to next view to close
VenueScreen.radiusChange = radiusChange;
}
}
Close in Next View:
//Close button Action
-(IBAction)DismissVenueScreen:(id)sender;
{
timer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(dismissVenueScreen) userInfo:nil repeats:YES];
NSLog(#"close button clciked");
}
//Circular trasition to Close window
-(void)dismissVenueScreen
{
int rChange = 0; // Its doing proper masking while changing this value
int radius = circleSize-rChange;
CAShapeLayer *circleShapeLayer = [CAShapeLayer layer];
// Make a circular shape
circleShapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(x+radiusChange,y+radiusChange, 2.0*radius, 2.0*radius)
cornerRadius:radius].CGPath;
// Configure the apperence of the circle
[[self.view layer] setMask:circleShapeLayer];
self.view.layer.masksToBounds = YES;
radiusChange = radiusChange+10;
circleSize = circleSize-10;
if (circleSize <= 0)
{
[timer invalidate]; //Stop titmer
[[self.view layer] setMask:nil];
[self.view removeFromSuperview];
circleSize = 480;
}
}
Related
I am messing around with a basic CAAnimation example where Im just trying to get a CALayer to rotate in a subclass of UIView. I start by making a new CALayer with a red background and inserting it into self.layer. I then make a CAAnimation that is supposed to be triggered on opacity changes and apply it to the CALayer, after which it spins as expected.
Weird thing is that if I try to apply the exact same animation using performSelector:withObject:afterDelay:, the CALayer no longer animates, although its opacity still changes. Even weirder is that if I use performSelector:withObject:afterDelay: to animate self.layer using pretty much the same code, it works.
Any ideas as to what's going on? Here is my code:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
point = [[CAGradientLayer alloc] init];
point.backgroundColor = [UIColor redColor].CGColor;
point.bounds = CGRectMake(0.0f, 0.0f, 30.0f, 20.0f);
point.position = CGPointMake(100.0f, 100.0f);
[self.layer addSublayer:point];
[point release];
[self performSelector:#selector(test) withObject:nil afterDelay:2];
}
return self;
}
-(void)test {
[self spinLayer:point];
// [self spinLayer:self.layer]; works here
}
-(CAAnimation*)animationForSpinning {
CATransform3D transform;
transform = CATransform3DMakeRotation(M_PI/2.0, 0, 0, 1.0);
CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
basicAnimation.toValue = [NSValue valueWithCATransform3D:transform];
basicAnimation.duration = 5;
basicAnimation.cumulative = NO;
basicAnimation.repeatCount = 10000;
return basicAnimation;
}
-(void)spinLayer:(CALayer*)layer {
CAAnimation *anim = [self animationForSpinning];
[layer addAnimation:anim forKey:#"opacity"];
layer.opacity = 0.6;
}
and this is my interface
#interface MyView : UIView {
CALayer *point;
}
-(void)test;
-(void)spinLayer:(CALayer*)layer;
-(CAAnimation*)animationForSpinning;
#end
Note: I am instantiating this view with a frame equal to [UIScreen mainScreen].applicationFrame from within the app delegate.
Change this:
[layer addAnimation:anim forKey:#"opacity"];
into this:
[layer addAnimation:anim
forKey:#"notOpacity!"];
What happens is that implicit animation layer.opacity = 0.6 overwrites you explicit animation. This happens because Core Animation uses property names when adding implicit animations.
(If you move layer.opacity = 0.6 above [layer addAnimation:anim forKey:#"opacity"]; it will also work, but layer.opacity will not get animated.)
And why it works without delay: because implicit animations are "enabled" only if layer is in the tree and been displayed.
I'm trying to slide down an image in an UIImageView, but I don't know which combination of UIContentMode and animation property is the right one to make that happen.
The image should always have the same size and should not be streched... all I want is, that nothing is visible first and then the frame extends and reveals the image.
Maybe it's easier if you see what I mean:
So, it sounds rather easy, but what UIContentMode should I use for the UIImageView and what property should I animate? Thank you!
I took your lead and made a screencast as well. Was this what you had in mind?
I put the animation repeating indefinitely so it would be easier to capture with a video, but it can be started at the press of a button, as well as frozen in place, showing the popover and its contents, until reversed to be hidden again.
I used Core Animation for that, instead of animating a UIView, since I wanted to use the mask property of CALayer to hide the popover and reveal it with a sliding animation.
Here is the code I used (same as in the video):
- (void)viewDidLoad
{
// Declaring the popover layer
CALayer *popover = [CALayer layer];
CGFloat popoverHeight = 64.0f;
CGFloat popoverWidth = 200.0f;
popover.frame = CGRectMake(50.0f, 100.0f, popoverWidth, popoverHeight);
popover.contents = (id) [UIImage imageNamed:#"popover.png"].CGImage;
// Declaring the mask layer
CALayer *maskLayer = [CALayer layer];
maskLayer.frame = CGRectMake(0, - popoverHeight, popoverWidth, popoverHeight);
maskLayer.backgroundColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f].CGColor;
// Setting the animation (animates the mask layer)
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position.y"];
animation.byValue = [NSNumber numberWithFloat:popoverHeight];
animation.repeatCount = HUGE_VALF;
animation.duration = 2.0f;
[maskLayer addAnimation:animation forKey:#"position.y"];
//Assigning the animated maskLayer to the mask property of your popover
popover.mask = maskLayer;
[self.view.layer addSublayer:popover];
[super viewDidLoad];
}
NOTE: You have to import the QuartzCore framework into your project and write this line in your header file: #import <QuartzCore/QuartzCore.h>.
Tells if this works for you or if you need any more help setting this up.
Try this code.
Consider the UIImageView as imageView.
imageView.contentMode = UIViewContentModeScaleAspectFill;
CGRect imageRect = imageView.frame;
CGRect origImgRect = imageRect;
imageRect.size.height = 5;
imageView.frame = imageRect;
[UIView animateWithDuration:2.0
animations:^{imageView.rect = origImgRect;}
completion:^(BOOL finished){ }];
i am working on a game to spot the difference between 2 images.
I want to add an effect when you spot it. drawing out a circle would be much better than just showing the circle suddenly. but i've never done core animation or opengl before.
i don't think preparing 100 sprites and changing the sprite frame by frame is a good idea.
here is my code: (just add a circle image to both left and right image. )
-(void) show {
CCSprite* leftCircle = [CCSprite spriteWithFile:#"circle.png"];
CCSprite* rightCircle = [CCSprite spriteWithFile:#"circle.png"];
leftCircle.scaleX = size.width / [leftCircle boundingBox].size.width;
leftCircle.scaleY = size.height / [leftCircle boundingBox].size.height;
rightCircle.scaleX = size.width / [rightCircle boundingBox].size.width;
rightCircle.scaleY = size.height / [rightCircle boundingBox].size.height;
leftCircle.anchorPoint = ccp(0, 1);
rightCircle.anchorPoint = ccp(0, 1);
leftCircle.position = leftPosition;
rightCircle.position = rightPosition;
[[GameScene sharedScene] addChild:leftCircle z: 3];
[[GameScene sharedScene] addChild:rightCircle z: 3];
shown = YES;
}
So how can i implement it? It would be great if you can provide some source code.
As a simple way i can recommend you to create a circle and put it's scale to zero. Then create a CCScale action and run it. It will give you a growing circle. Here is the code:
CCSprite *sprite = [CCSprite spriteWithFile:#"mySprite.png"];
[sprite setScale:0.01];
id scale = [CCScale actionWithDuration:0.3 scale:1];
[sprite runAction:scale];
The other action can be used is CCFadeIn.You can make your sprite invisible after creation and make it fade in:
CCSprite *sprite = [CCSprite spriteWithFile:#"mySprite.png"];
[sprite setOpacity:0];
id fade = [CCFadeIn actionWithDuration:0.3];
[sprite runAction:fade];
Also you can combine this actions:
[sprite runAction:fade];
[sprite runAction:scale];
Also you can make it big (set scale 3 for example) and transparent. And make it fade in and scale down to highlight your image
To Draw a circle via drawing effect you can make it from small parts (arcs) and then build your circle from them. I think it also will be cool if you make don't make the part visible when adding, but make it fade in. I mean something like this:
-(void) init
{
NSMutableArray *parts = [[NSMutableArray array] retain]; //store it in your class variable
parts_ = parts;
localTime_ = 0; //float localTime_ - store the local time in your layer
//create all the parts here, make them invisible and add to the layer and parts
}
-(void) update: (CCTime) dt //add update method to your layer that will be called every simulation step
{
localTime_ += dt;
const float fadeTime = 0.1;
int currentPart = localTime_ / fadeTime;
int i = 0;
for (CCSprite *part in parts)
{
//setup the opacity of each part according to localTime
if (i < currentPart) [part setOpacity:255];
else if (i == currentPart)
{
float localLocalTime = localTime - i*fadeTime;
float alpha = localLocalTime / fadeTime;
[part setOpacity:alpha];
}
++i;
}
}
It is rather simple to create the effect you want.
You can realize it by setting the strokeEnd of a CAShapeLayer.
Here are the details:
create a path you then assign to a CAShapeLayer
UIBezierPath* circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100.0, 100.0) radius:80.0 startAngle:DEGREES_TO_RADIANS(270) endAngle:DEGREES_TO_RADIANS(270.01) clockwise:NO];
circle = [CAShapeLayer layer];
circle.path = circlePath.CGPath;
circle.strokeColor = [[UIColor blackColor] CGColor];
circle.fillColor = [[UIColor whiteColor] CGColor];
circle.lineWidth = 6.0;
circle.strokeEnd = 0.0;
[self.view.layer addSublayer:circle];
set the strokeEnd to implicitly animate the drawing of the circle
circle.strokeEnd = 1.0;
That's it! The circle will be automatically drawn for you ;). Here is the code from above with a GestureRecognizer to trigger the animation. Copy this to an empty project of type "Single View Application" and paste it to the ViewController.
#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIBezierPath* circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(100.0, 100.0) radius:80.0 startAngle:DEGREES_TO_RADIANS(270) endAngle:DEGREES_TO_RADIANS(270.01) clockwise:NO];
circle = [CAShapeLayer layer];
circle.path = circlePath.CGPath;
circle.strokeColor = [[UIColor blackColor] CGColor];
circle.fillColor = [[UIColor whiteColor] CGColor];
circle.lineWidth = 6.0;
circle.strokeEnd = 0.0;
[self.view.layer addSublayer:circle];
// add a tag gesture recognizer
// add a single tap gesture recognizer
UITapGestureRecognizer* tapGR = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapGesture:)];
[self.view addGestureRecognizer:tapGR];
}
#pragma mark - gesture recognizer methods
//
// GestureRecognizer to handle the tap on the view
//
- (void)handleTapGesture:(UIGestureRecognizer *)gestureRecognizer {
circle.strokeEnd = 1.0;
}
I'm trying to move and rotate a UIImageView. I'm not using Core Animation for this nor I can use it.
By the way, the core part of the animation is what follows:
CGRect r = self.frame;
r.origin.y -= self.gravity + spring;
r.origin.x -= 1;
self.rotation -= 0.015;
self.transform = CGAffineTransformMakeRotation(rotation);
self.frame = r;
It almost works. I does move or it does rotate in place, but when I try to move and rotate it, it completely deforms skewing.
I've tried to move and then rotate and viceversa. I also tried various values for content mode with no luck. Any help?
EDIT
useful tutorial: http://www.permadi.com/blog/2009/05/iphone-sdk-bouncing-and-rotating-ball-example/
It's hard to be sure what's going on without a picture of what you are seeing, but the following may be useful:
When you apply a rotation to a view it rotates it around (0,0), so you need to make sure you set the bounds of your view so that (0,0) is where you want the center of rotation. Suppose the image you are rotating is a 100x200 image and you want to rotate around the center of the image then you would need to say self.bounds = CGMakeRect(-50, -100, 100, 200); If you don't do this it will rotate about it's upper-left corner making sort of a spiral.
Oh, and using self.center to set the view's position seems to be more predictable than using self.frame when you have applied a rotation.
Here's some demo code that might make it easier to understand. In this case the code that does the animation is the view controller, but you should get the idea:
#define USE_SELF_CENTER 0
- (void)viewDidLoad {
[super viewDidLoad];
self.rotation = 0.0f;
self.translation = CGPointMake(self.view.bounds.size.width/2.0f, 0.0);
ObjectView* ov = [[ObjectView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
#if USE_SELF_CENTER==1
ov.center = self.translation;
#else
ov.transform = CGAffineTransformMakeTranslation(self.translation.x, self.translation.y);
#endif
// adjust bounds for center of rotation
ov.bounds = CGRectMake(-50.0f, -50.0f, 100.0f, 100.0f);
[self.view addSubview:ov];
self.objectView = ov;
[ov release];
NSTimer *aTimer = [NSTimer timerWithTimeInterval:0.5 target:self selector:#selector(nextFrame)
userInfo:nil repeats:YES];
self.timer = aTimer;
[aTimer release];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)nextFrame
{
NSLog(#"nextFrame");
self.rotation += (5.0 / 180.0f * M_PI);
self.translation = CGPointMake(self.translation.x, self.translation.y + 2.0);
#if USE_SELF_CENTER==1
CGAffineTransform t = CGAffineTransformMakeRotation(self.rotation);
self.objectView.center = self.translation;
self.objectView.transform = t;
#else
CGAffineTransform rot = CGAffineTransformMakeRotation(self.rotation);
CGAffineTransform txy = CGAffineTransformMakeTranslation(self.translation.x, self.translation.y);
CGAffineTransform t = CGAffineTransformConcat(rot, txy);
self.objectView.transform = t;
#endif
}
The code demonstrates two ways of doing what you want. If USE_SELF_CENTER is zero then all the animation is done via transforms. If USE_SELF_CENTER is non-zero a transform is used for rotation and the translation is accomplished by setting the center of the view.
use something like this, the idea is combine both the transformation i.e.
CGAffineTransform transforms = CGAffineTransformConcat(CGAffineTransformMakeTranslation(-1, -(self.gravity + spring)),CGAffineTransformMakeRotation(rotation));
self.transform = transforms;
Hope this will work
Hello Friend please use this steps. On your XIB, take UIImageView and set Image and set its IBOutlet.
In .h file
int theta;
NSTimer *tmrAnimation;
IBOutlet UIImageView *imgViewLoader;
In .m file
- (void)viewDidLoad
{
[super viewDidLoad];
theta = 0;
tmrAnimation = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(animationImageViewLoader) userInfo:nil repeats:YES];
}
Add This function in your .m file.
-(void)animationImageViewLoader
{
CGFloat angle = theta * (PI / 100);
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
theta = (theta + 1) % 200;
imgViewLoader.transform = transform;
}
This code will help for Image Rotation only....
Following code helped me in achieving translation(vertical movement) and rotation together.
CGAffineTransform transform = CGAffineTransformMakeTranslation(0, -200);
transform = CGAffineTransformRotate(transform, M_PI);
[UIView transitionWithView:self
duration:0.5
options:UIViewAnimationOptionAllowAnimatedContent<br>
animations:^{
[view setTransform:transform];
}
completion:^(BOOL finished){
}];
Hope this will help someone else...
I'm brand new in iPhone animation feature. I wrote a simple testing program to spin a red rectangle; however, the animation doesn't act at all. What's wrong with my code? Thanks a lot...
//.h
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#define DegreesToRadians(X) (M_PI*X/180.0)
//.m
- (void)viewDidLoad {
[super viewDidLoad];
CGRect theRect = CGRectMake(0,0,100,100);
UIView *theBox = [[UIView alloc] initWithFrame:theRect];
theBox.backgroundColor = [UIColor redColor];
[self.view addSubview:theBox];
CALayer *theLayer = [theBox layer];
[self spinLayer:theLayer];
}
-(CAAnimation *)animationForSpinning{
float radians = DegreesToRadians(360);
CATransform3D theTransform = CATransform3DMakeRotation(radians, 0, 0, 1.0);
CABasicAnimation *theAnimation = [CABasicAnimation animationWithKeyPath:#"transform"];
theAnimation.toValue = [NSValue valueWithCATransform3D:theTransform];
theAnimation.duration = 2; // 2 seconds
theAnimation.repeatCount = 10000;
theAnimation.cumulative = YES;
return theAnimation;
}
-(void)spinLayer:(CALayer *)theLayer{
CAAnimation *spinningAnimation = [self animationForSpinning];
[theLayer addAnimation:spinningAnimation forKey:#"opacity"];
// trigger the animation
theLayer.opacity = 0.99;
}
Your animation is not running because Core Animation sees a transform of 360 degrees being the same as your original image, so it does nothing.
The easiest way to handle a 360 degree rotation is described in this answer to this question, where you set up your CABasicAnimation to animate the transform.rotation.z property from 0 to 2 * pi radians.