Making a single image to scroll infinitely over the viewcontroller? - ios

I have two images. first one is something like a transparent hole and should be static while the second one is scooter like image which needs to continuously scrolling horizontally that should give a effect of video.
Can anybody suggest me what to do to achieve the same.

Please check the code Infinity Scroll

You can animate X position of your image view like this:
CGPoint point0 = imView.layer.position;
CGPoint point1 = { NSIntegerMax, point0.y };
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position.x"];
anim.fromValue = #(point0.x);
anim.toValue = #(point1.x);
anim.duration = 1.5f;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// First we update the model layer's property.
imView.layer.position = point1;
// Now we attach the animation.
[imView.layer addAnimation:anim forKey:#"position.x"];
For infinite scrolling, set your scrollview' contentSize correctly:
scrollView.contentSize = CGSizeMake(NSIntegerMax, kScreenHeight); // Deduce kScreenHeight based on device height

Related

CABasicAnimation appears offscreen by some margin

So I am trying CABasicAnimation for the very first time. I am trying to apply it on a layer and I want layer to interpolate from one point to another. I initially set a negative x coordinate and I put the animation on the infinite. It is working but then it takes few seconds to appear on screen. So it should completely go to the end and start again. Following is code
[[self bgImageView] setImage:[UIImage imageNamed:#"bg_dynamicwx_cloudyday_3.jpg"]];
for (CALayer *layer in self.emitterView.layer.sublayers) {
[layer removeAllAnimations];
}
[self.emitterView.layer removeAllAnimations];
NSArray *subLayers = self.emitterView.layer.sublayers;
for (CALayer *layer in subLayers) {
[layer removeFromSuperlayer];
}
CALayer *mask = [CALayer layer];
UIImage *layerImg = [UIImage imageNamed:#"2_cloud.png"];
mask.bounds = CGRectMake(self.emitterView.bounds.origin.x, self.emitterView.bounds.origin.y, layerImg.size.width, layerImg.size.height);
mask.contents = (id)layerImg.CGImage;
CGPoint fromPoint = CGPointMake(-layerImg.size.width, 0);
CGPoint toPoint = CGPointMake(layerImg.size.width, 0);
mask.position = fromPoint; // CoreAnimation animations do *not* persist
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:fromPoint];
animation.toValue = [NSValue valueWithCGPoint:toPoint];
animation.duration = 150;
animation.repeatCount = INFINITY;
[mask addAnimation:animation forKey:#"position"];
[self.emitterView.layer addSublayer:mask];
Basically I'm trying the cloud animation, where the cloud is floating over the screen. I'm pretty sure I got some concept wrong here for basic animation. As the duration is 150 (it is supposed to be subtle), it takes forever to show up the animation. Seems like position is offscreen by more than what I have given. Image I am using for the layer is 728 by 368.
From the documentation:
The position property is located in the middle of the layer.
The code you're using assumes that position is located at the top left of the layer. You can easily fix this by changing your to and from points:
CGPoint fromPoint = CGPointMake(-layerImg.size.width*0.5, layerImg.size.height*0.5);
CGPoint toPoint = CGPointMake(layerImg.size.width*1.5, layerImg.size.height*0.5);
Now your layer will animate from off-screen left to off-screen right.

How to get perfect X , Y position of rotating UIView in iOS

I am using CABasicanimation for rotate an UIView. I am using this code:
CABasicAnimation *rotationAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
NSNumber *currentAngle = [CircleView.layer.presentationLayer valueForKeyPath:#"transform.rotation"];
rotationAnimation.fromValue = currentAngle;
rotationAnimation.toValue = #(50*M_PI);
rotationAnimation.duration = 50.0f; // this might be too fast
rotationAnimation.repeatCount = HUGE_VALF; // HUGE_VALF is defined in math.h so import it
[CircleView.layer addAnimation:rotationAnimation forKey:#"rotationAnimationleft"];
in this I want perfect X,Y position while "UIView" is rotating. How can I achieve this?
I Got A Answer Of This Question Please Go To This Link For Answer
Go to This Link For Answer
So, if I understand you correctly, you just want to get the X and Y coordinates of your view during rotation?
You can do this by logging the frame of the presentationLayer, like this:
- (IBAction)buttonAction:(UIButton *)sender {
CGRect position = [[CircleView.layer presentationLayer] frame];
NSLog(#"%#", NSStringFromCGRect(position));
}

iOS animate continuous background?

I'm creating an application for iOS6 and 7. I need to make a continuos background, meant: i got 2 pictures i want to loop, and make the "infinity" view. I have tried a lot of code examples, but every time my iPhone just go black when opening the application.
Thanks in advance.
Here is the code i tried so far:
UIImage *cloudImage = [UIImage imageNamed:#"cloud.png"];
CALayer *cloud = [CALayer layer];
cloud.contents = (id)cloudImage.CGImage;
cloud.bounds = CGRectMake(0, 0, cloudImage.size.width, cloudImage.size.height);
cloud.position = CGPointMake(self.view.bounds.size.width / 2,
cloudImage.size.height / 2);
[self.view.layer addSublayer:cloud];
CGPoint startPt = CGPointMake(self.view.bounds.size.width + cloud.bounds.size.width / 2,
cloud.position.y);
CGPoint endPt = CGPointMake(cloud.bounds.size.width / -2,
cloud.position.y);
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position"];
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
anim.fromValue = [NSValue valueWithCGPoint:startPt];
anim.toValue = [NSValue valueWithCGPoint:endPt];
anim.repeatCount = HUGE_VALF;
anim.duration = 8.0;
[cloud addAnimation:anim forKey:#"position"];
But i need to have 2 photos in a infinity loop. and that background image are scrolling up instead of sideways
I think instead of the pictures you are making loop with empty views. So your screen will completely filled with those views. That is why your screen going to black. So before starting the infinite loop of views check for the pictures which is there or not.
where did you add this code?
some times viewDidLoad is not a good place for adding view codes!
try adding a code in viewDidLayoutSubviews or with a little delay to insure that the images load properly.
try this instead [self.view.layer addSublayer:cloud];
[self.view addSubview:cloud];

Move UIImage across screen using core animation

DO NOT POST ANSWERS ABOUT UIVIEW ANIMATIONS I understand UIView, I am learning core-animation. Im trying to get an image to move 50 units to the right but I am having many issues. First when the animation is called the image jumps to a new location, runs, then jumps back to the original location. I want it to simply move 50 units to the right, stop, move again if the button is pressed. I have spent a lot of time researching and I can't seem to find the problem. My Code:
-(IBAction)preform:(id)sender{
CGPoint point = CGPointMake(imView.frame.origin.x, imView.frame.origin.y);
imView.layer.position = point;
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position.x"];
anim.fromValue = [NSValue valueWithCGPoint:point];
anim.toValue = [NSValue valueWithCGPoint:CGPointMake(point.x + 50, point.y)];
anim.duration = 1.5f;
anim.repeatCount =1;
anim.removedOnCompletion = YES;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[imView.layer addAnimation:anim forKey:#"position.x"];
imView.layer.position = point;
}
I see several problems in your code.
First, you're grabbing the frame origin like this:
CGPoint point = CGPointMake(imView.frame.origin.x, imView.frame.origin.y);
Although it's not really a problem, you could simply do this:
CGPoint point = imView.frame.origin;
The problem is that you're setting the layer's position to its frame's origin. But by default, the position controls the center of the view, and the frame's origin is its upper-left corner. You probably want to just pick up the layer's position in the first place:
CGPoint point = imView.layer.position; // equivalent to imView.center
Second, you're using the position.x key path, which wants a CGFloat value, but you're providing a CGPoint value. This doesn't appear to cause a problem in the iOS 6.1 simulator, but it's probably a bad idea to assume it will always work.
Third, you need to understand that an animation does not change the properties of your layer! Each of the layers you normally manipulate (technically called a model layer) has an associated presentation layer. The presentation layer's properties control what is on the screen. When you change a model layer's property, Core Animation usually sets up an animation (from the old value to the new value) on the presentation layer automatically. This is called an implicit animation. A UIView suppresses implicit animations on its layer.
When the animation on the presentation layer ends and is removed, the presentation layer's properties revert to its model layer's values. So to make the change permanent, you need to update the model layer's properties. Generally it's best to update the model layer's properties first, then add the animation, so that your explicit animation overwrites the implicit animation (if one was created).
As it happens, although you can animate position.x, you need to set position on the model layer to make it stick. I tested this to work:
- (IBAction)perform:(id)sender {
CGPoint point0 = imView.layer.position;
CGPoint point1 = { point0.x + 50, point0.y };
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position.x"];
anim.fromValue = #(point0.x);
anim.toValue = #(point1.x);
anim.duration = 1.5f;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
// First we update the model layer's property.
imView.layer.position = point1;
// Now we attach the animation.
[imView.layer addAnimation:anim forKey:#"position.x"];
}
(Note that I called my method perform:. The method in the original post seems to be misspelled.)
If you want to really understand Core Animation, I highly recommend watching the Core Animation Essentials video from WWDC 2011. It's an hour long and contains a ton of useful information.
This is what you should need to get it working. The position on the layer is already set so you can use that as the fromValue and just modify it to get the toValue. The other important step is to set the layers position to be endPos so that when the animation finishes, the image view will stay at the correct position.
-(IBAction)preform:(id)sender
{
CGPoint startPos = imView.layer.position;
CGPoint endPos = CGPointMake(point.x + 50, point.y);
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position.x"];
anim.fromValue = [NSValue valueWithCGPoint:startPos];
anim.toValue = [NSValue valueWithCGPoint:endPos];
anim.duration = 1.5f;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[imView.layer addAnimation:anim forKey:#"position.x"];
imView.layer.position = endPos;
}
jumping back is caused by the fact that you remove animation on completion
anim.removeOnCompletion = NO;
EDIT #3: just copied and tried your code here is the fix for all of your issues, pretty much self explanatory :
CGPoint point = imView.center;
CABasicAnimation *anim = [CABasicAnimation animationWithKeyPath:#"position.x"];
anim.fromValue = [NSNumber numberWithFloat:point.x];
anim.toValue = [NSNumber numberWithFloat:point.x+50.0];//[NSValue valueWithCGPoint:CGPointMake(point.x + 50, point.y)];
anim.duration = 1.5f;
anim.repeatCount =1;
anim.removedOnCompletion = NO;
anim.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
anim.fillMode=kCAFillModeForwards;
[imView.layer addAnimation:anim forKey:#"position.x"];
[sender.layer addAnimation:anim forKey:#"position.x"];
sender.layer.position=CGPointMake(point.x+50.0,sender.layer.position.y);
That fact that it jumps back even though removedOnCompletion is set to no is a head scratcher. Still playing around with it.

Cannot get current position of CALayer during animation

I am trying to achieve an animation that when you hold down a button it animates a block down, and when you release, it animates it back up to the original position, but I cannot obtain the current position of the animating block no matter what. Here is my code:
-(IBAction)moveDown:(id)sender{
CGRect position = [[container.layer presentationLayer] frame];
[movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)];
[movePath addLineToPoint:CGPointMake(container.frame.origin.x, 310)];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = NO;
moveAnim.fillMode = kCAFillModeForwards;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil];
animGroup.duration = 2.0;
animGroup.removedOnCompletion = NO;
animGroup.fillMode = kCAFillModeForwards;
[container.layer addAnimation:animGroup forKey:nil];
}
-(IBAction)moveUp:(id)sender{
CGRect position = [[container.layer presentationLayer] frame];
UIBezierPath *movePath = [UIBezierPath bezierPath];
[movePath moveToPoint:CGPointMake(container.frame.origin.x, position.y)];
[movePath addLineToPoint:CGPointMake(container.frame.origin.x, 115)];
CAKeyframeAnimation *moveAnim = [CAKeyframeAnimation animationWithKeyPath:#"position"];
moveAnim.path = movePath.CGPath;
moveAnim.removedOnCompletion = NO;
moveAnim.fillMode = kCAFillModeForwards;
CAAnimationGroup *animGroup = [CAAnimationGroup animation];
animGroup.animations = [NSArray arrayWithObjects:moveAnim, nil];
animGroup.duration = 2.0;
animGroup.removedOnCompletion = NO;
animGroup.fillMode = kCAFillModeForwards;
[container.layer addAnimation:animGroup forKey:nil];
}
But the line
CGRect position = [[container.layer presentationLayer] frame];
is only returning the destination position not the current position. I need to basically give me the current position of the container thats animating once I release the button, so I can perform the next animation. What I have now does not work.
I haven't analyzed your code enough to be 100% sure why [[container.layer presentationLayer] frame] might not return what you expect. But I see several problems.
One obvious problem is that moveDown: doesn't declare movePath. If movePath is an instance variable, you probably want to clear it or create a new instance each time moveDown: is called, but I don't see you doing that.
A less obvious problem is that (judging from your use of removedOnCompletion and fillMode, in spite of your use of presentationLayer) you apparently don't understand how Core Animation works. This turns out to be surprisingly common, so forgive me if I'm wrong. Anyway, read on, because I will explain how Core Animation works and then how to fix your problem.
In Core Animation, the layer object you normally work with is a model layer. When you attach an animation to a layer, Core Animation creates a copy of the model layer, called the presentation layer, and the animation changes the properties of the presentation layer over time. An animation never changes the properties of the model layer.
When the animation ends, and (by default) is removed, the presentation layer is destroyed and the values of the model layer's properties take effect again. So the layer on screen appears to “snap back” to its original position/color/whatever.
A common, but wrong way to fix this is to set the animation's removedOnCompletion to NO and its fillMode to kCAFillModeForwards. When you do this, the presentation layer hangs around, so there's no “snap back” on screen. The problem is that now you have the presentation layer hanging around with different values than the model layer. If you ask the model layer (or the view that owns it) for the value of the animated property, you'll get a value that's different than what's on screen. And if you try to animate the property again, the animation will probably start from the wrong place.
To animate a layer property and make it “stick”, you need to change the model layer's property value, and then apply the animation. That way, when the animation is removed, and the presentation layer goes away, the layer on screen will look exactly the same, because the model layer has the same property values as its presentation layer had when the animation ended.
Now, I don't know why you're using a keyframe to animate straight-line motion, or why you're using an animation group. Neither seems necessary here. And your two methods are virtually identical, so let's factor out the common code:
- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y {
CGPoint fromValue = [layer.presentationLayer position];
CGPoint toValue = CGPointMake(fromValue.x, y);
layer.position = toValue;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:fromValue];
animation.toValue = [NSValue valueWithCGPoint:toValue];
animation.duration = 2;
[layer addAnimation:animation forKey:animation.keyPath];
}
Notice that we're giving the animation a key when I add it to the layer. Since we use the same key every time, each new animation will replace (remove) the prior animation if the prior animation hasn't finished yet.
Of course, as soon as you play with this, you'll find that if you moveUp: when the moveDown: is only half finished, the moveUp: animation will appear to be at half speed because it still has a duration of 2 seconds but only half as far to travel. We should really compute the duration based on the distance to be travelled:
- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
CGPoint fromValue = [layer.presentationLayer position];
CGPoint toValue = CGPointMake(fromValue.x, y);
layer.position = toValue;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"position"];
animation.fromValue = [NSValue valueWithCGPoint:fromValue];
animation.toValue = [NSValue valueWithCGPoint:toValue];
animation.duration = 2.0 * (toValue.y - fromValue.y) / (y - baseY);
[layer addAnimation:animation forKey:animation.keyPath];
}
If you really need it to be a keypath animation in an animation group, your question should show us why you need those things. Anyway, it works with those things too:
- (void)animateLayer:(CALayer *)layer toY:(CGFloat)y withBaseY:(CGFloat)baseY {
CGPoint fromValue = [layer.presentationLayer position];
CGPoint toValue = CGPointMake(fromValue.x, y);
layer.position = toValue;
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:fromValue];
[path addLineToPoint:toValue];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:#"position"];
animation.path = path.CGPath;
animation.duration = 2.0 * (toValue.y - fromValue.y) / (y - baseY);
CAAnimationGroup *group = [CAAnimationGroup animation];
group.duration = animation.duration;
group.animations = #[animation];
[layer addAnimation:group forKey:animation.keyPath];
}
You can find the full code for my test program in this gist. Just create a new Single View Application project and replace the contents of ViewController.m with the contents of the gist.

Resources