Hello I have an image that I'd like to move up and down (Up 10 pixels and down 10 pixels) so that my image appears to be hovering. How can I do this with simple animation thanks so much!!!
You could use Core Animation to animate the position of the views layer. If you configure the animation to be additive you won't have to bother calculating the new absolute position, just the change (relative position).
CABasicAnimation *hover = [CABasicAnimation animationWithKeyPath:#"position"];
hover.additive = YES; // fromValue and toValue will be relative instead of absolute values
hover.fromValue = [NSValue valueWithCGPoint:CGPointZero];
hover.toValue = [NSValue valueWithCGPoint:CGPointMake(0.0, -10.0)]; // y increases downwards on iOS
hover.autoreverses = YES; // Animate back to normal afterwards
hover.duration = 0.2; // The duration for one part of the animation (0.2 up and 0.2 down)
hover.repeatCount = INFINITY; // The number of times the animation should repeat
[myView.layer addAnimation:hover forKey:#"myHoverAnimation"];
Since this is using Core Animation you will need to add QuartzCore.framework and #import <QuartzCore/QuartzCore.h> into your code.
For Swift 3, Swift 4 and Swift 5:
let hover = CABasicAnimation(keyPath: "position")
hover.isAdditive = true
hover.fromValue = NSValue(cgPoint: CGPoint.zero)
hover.toValue = NSValue(cgPoint: CGPoint(x: 0.0, y: 100.0))
hover.autoreverses = true
hover.duration = 2
hover.repeatCount = Float.infinity
myView.layer.add(hover, forKey: "myHoverAnimation")
Try this:
CGRect frm_up = imageView.frame;
frm_up.origin.y -= 10;
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeats
animations:^{
imageView.frame = frm_up;
}
completion:NULL
];
For such a task, it will be better to place an image in a sublayer of UIView, and then animate that sublayer frame. This tutorial could give you some idea: http://www.raywenderlich.com/2502/introduction-to-calayers-tutorial
Related
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
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];
I'm trying to animate the bounds of a CALayer and it's not working. Here's the code:
class CircleView < UIIVew
def initWithFrame(frame)
super
# determine the dimensions
radius = (bounds.size.width < bounds.size.height ? bounds.size.width : bounds.size.height) / 2
# create the circle layer
#circle = CAShapeLayer.layer
#circle.bounds = bounds
#circle.path = UIBezierPath.bezierPathWithRoundedRect(bounds, cornerRadius: radius).CGPath
#circle.fillColor = UIColor.blueColor.CGColor
# add the circle to the view
self.layer.addSublayer(#circle)
shrink
self
end
private
# Applies the shrink animation to the provided circle layer.
def shrink
# determine the bounds
old_bounds = #circle.bounds
new_bounds = CGRectMake(old_bounds.size.width / 2, old_bounds.size.height / 2, 0, 0)
# animate the shrinking
animation = CABasicAnimation.animationWithKeyPath("bounds")
animation.fromValue = NSValue.valueWithCGRect(old_bounds)
animation.toValue = NSValue.valueWithCGRect(new_bounds)
animation.duration = 3.0
#circle.addAnimation(animation, forKey:"bounds")
# set the bounds so the animation doesn't bounce back when it's complete
#circle.bounds = new_bounds
end
# Applies the shrink animation to the provided circle layer.
def disappear
# animate the shirnk
animation = CABasicAnimation.animationWithKeyPath("opacity")
animation.fromValue = NSNumber.numberWithFloat(1.0)
animation.toValue = NSNumber.numberWithFloat(0.0)
animation.duration = 3.0
#circle.addAnimation(animation, forKey:"opacity")
# set the value so the animation doesn't bound back when it's completed
#circle.opacity = 0.0
end
end
If I call disappear from the initWithFrame method, the animation works fine. However, calling shrink does nothing. What am I doing wrong?
That's because you are using a CAShapeLayer. The path is independent of the bounds, so you have to animate it separately.
I could reproduce your problem with a version of your code roughly translated to Objective C. When I changed the shrinkmethod to this, it worked:
-(void)shrink
{
CGRect old_bounds = self.circle.bounds;
CGRect new_bounds = CGRectMake(old_bounds.size.width / 2, old_bounds.size.height / 2, 0, 0);
// bounds
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath: #"bounds"];
animation.fromValue = [NSValue valueWithCGRect: old_bounds];
animation.toValue = [NSValue valueWithCGRect: new_bounds];
animation.duration = 3.0;
[self.circle addAnimation: animation forKey: #"bounds"];
// path
CGPathRef old_path = self.circle.path;
CGPathRef new_path = [UIBezierPath bezierPathWithRoundedRect:new_bounds cornerRadius:0].CGPath;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath: #"path"];
pathAnimation.fromValue = (__bridge id)(old_path);
pathAnimation.toValue = (__bridge id)(new_path);
pathAnimation.duration = 3.0;
[self.circle addAnimation: pathAnimation forKey: #"path"];
//set the value so the animation doesn't bound back when it's completed
self.circle.bounds = new_bounds;
self.circle.path = new_path;
}
Technically, you might not even have to animate the bounds. If you set a distinct backgroundColor on your circle layer, you can experiment a little and more easily see how boundsand path can be set and animated more or less independently (for example, in your original code you could see that the bounds are in fact animated but the path stays the same).
Not knowing Ruby, I would suggest adjusting the frame rather than the bounds. The bounds are in the CALayers own coordinate system, and have no bearing on how the layer itself is displayed in the parent.
I have a UIView and two UISwipeGesture classes that animate the X rotation between 0 and -99, giving the flip board effect. If the user swipes down and then immediately swipes back up, the 'swipe down' animation is ended prematurely and it begings the swipe up animation.
How can I tell if it gets ended prematurely due to another animation being added? The animationDidStop:finished message gets sent, but the finished value is always TRUE.
Here's swipe down code:
CATransform3D transform = CATransform3DIdentity;
// This must be set before we calculate the transforms to give the 3D perspective (1.0 / -DISTANCE_FROM_CAMERA)
transform.m34 = 1.0 / -4000;
// Rotate on the X axis
transform = CATransform3DRotate(transform, DegreesToRadians(-99), 1, 0, 0);
// Apply transform in an animation
CABasicAnimation* foldDownAnimatnion = [CABasicAnimation animationWithKeyPath:#"transform"];
foldDownAnimatnion.duration = 1;
foldDownAnimatnion.toValue = [NSValue valueWithCATransform3D:transform];
foldDownAnimatnion.removedOnCompletion = NO;
foldDownAnimatnion.fillMode = kCAFillModeForwards;
foldDownAnimatnion.delegate = self;
// Identify this animation in delegate method
[foldDownAnimatnion setValue:#"foldDown" forKey:#"name"];
[foldDownAnimatnion setValue:theLayer forKey:#"layer"];
[theLayer addAnimation:foldDownAnimatnion forKey:nil];
And my delegate method:
- (void)animationDidStop:(CAAnimation *)animation finished:(BOOL)finished
{
if([[animation valueForKey:#"name"] isEqualToString:#"foldDown"])
{
// Why is this always YES??
NSLog(#"Animation finished: %#", (finished)?#"Yes" : #"No");
}
else if([[animation valueForKey:#"name"] isEqualToString:#"foldUp"])
{
NSLog(#"animationDidStop: foldUp");
}
}
Add two BOOL instance variable(BOOL UpAnimationStarted; BOOL DownAnimationStarted;) In the beginning of SwipeUp set UpAnimationStarted to YES. And In the beginning of SwipeDown set DownAnimationStarted to YES. You can use these BOOL values to check animation interruption.
Thanks to #DavidRönnqvist for pointing me in the right direction, I went back to my O'Reilly book on iOS4 and redid the animation, the code now much cleaner:
Swipe Down:
CATransform3D transform = CATransform3DIdentity;
// This must be set before we calculate the transforms to give the 3D perspective (1.0 / -DISTANCE_FROM_CAMERA)
transform.m34 = 1.0 / -4000;
// Rotate on the X axis
transform = CATransform3DRotate(transform, DegreesToRadians(-99), 1, 0, 0);
// Apply transform in an animation
[CATransaction setDisableActions:YES];
theLayer.transform = transform;
CABasicAnimation* foldDownAnimatnion = [CABasicAnimation animationWithKeyPath:#"transform"];
foldDownAnimatnion.duration = 1;
foldDownAnimatnion.delegate = self;
[foldDownAnimatnion setValue:#"foldDown" forKey:#"name"];
[foldDownAnimatnion setValue:theLayer forKey:#"layer"];
[theLayer addAnimation:foldDownAnimatnion forKey:#"foldDown"];
Swipe Up
[theLayer removeAnimationForKey:#"foldDown"];
CATransform3D transform = CATransform3DIdentity;
// This must be set before we calculate the transforms to give the 3D perspective (1.0 / -DISTANCE_FROM_CAMERA)
transform.m34 = 1.0 / -4000;
// Rotate on the X axis
transform = CATransform3DRotate(transform, DegreesToRadians(0), 1, 0, 0);
// Apply transform in an animation
[CATransaction setDisableActions:YES];
theLayer.transform = transform;
CABasicAnimation* animatnion = [CABasicAnimation animationWithKeyPath:#"transform"];
animatnion.duration = 1;
animatnion.delegate = self;
[animatnion setValue:#"foldUp" forKey:#"name"];
[theLayer addAnimation:animatnion forKey:#"foldUp"];
The key really is that I am applying the new transform to the layer, and then animating it. What I was doing before was applying the animation to the layer and making it fill forwards, this caused problems when I removed it mid-way.
What I'm trying to do seems like it should be easy enough: I created a 2D top-down view of an old phonograph record. I want to rotate it (lay it back) in its X axis and then spin it around its Z axis.
I've read every question here that has CATransform3D in the body, I've read Steve Baker's "Matrices can be your friends" article as well as Bill Dudney's book "Core Animation for Mac OS X and the iPhone" I think Brad Larson's "3-D Rotation without a trackball" has all the right code, but since he's permitting the user to adjust all three axis, I'm having a hard time shrinking his code into what I perceive to be just one dimension (a rotated z axis).
Here's the image I'm testing with, not that the particulars are important to the problem:
I bring that onscreen the usual way: (in a subclass of UIView)
- (void)awakeFromNib
{
UIImage *recordImage = [UIImage imageNamed:#"3DgoldRecord"];
if (recordImage) {
recordLayer = [CALayer layer];
[recordLayer setFrame:CGRectMake(0.0, 0.0, 1024, 1024)];
[recordLayer setContents:(id)[[UIImage imageNamed:#"3DgoldRecord"] CGImage]];
[self.layer addSublayer:recordLayer];
}
}
That's the first test, just getting it on the screen ;-)
Then, to "lay it back" I apply a transform to rotate about the layer's X axis, inserting this code after setting the contents of the layer to the image and before adding the sublayer:
CATransform3D myRotationTransform =
CATransform3DRotate(recordLayer.transform,
(M_PI_2 * 0.85), //experiment with flatness
1.0, // rotate only across the x-axis
0.0, // no y-axis transform
0.0); // no z-axis transform
recordLayer.transform = myRotationTransform;
That worked as expected: The record is laying back nicely.
And for the next step, causing the record to spin, I tied this animation to the touchesEnded event, although once out of the testing/learning phase this rotation won't be under user control:
CATransform3D currentTransform = recordLayer.transform; // to come back to at the end
CATransform3D myRotationTransform =
CATransform3DRotate(currentTransform,
1.0, // go all the way around once
(M_PI_2 * 0.85), // x-axis change
1.00, // y-axis change ?
0.0); // z-axis change ?
recordLayer.transform = myRotationTransform;
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:#"transform.rotation"];
myAnimation.duration = 5.0;
myAnimation.fromValue = [NSNumber numberWithFloat:0.0];
myAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2.0];
myAnimation.delegate = self;
[recordLayer addAnimation:myAnimation forKey:#"transform.rotation"];
So I'm pretty sure what I'm hung up on is the vector in the CATransform3DRotate call (trust me: I've been trying simple changes in that vector to watch the change... what's listed there now is simply the last change I tried). As I understand it, the values for x, y, and z in the transform are, in essence, the percentage of the value passed in during the animation ranging from fromValue to toValue.
If I'm on the right track understanding this, is it possible to express this in a single transform? Or must I, for each effective frame of animation, rotate the original upright image slightly around the z axis and then lay the result down with an x axis rotation? I saw a question/answer that talked about combining transforms, but that was a scale transform followed by a rotation transform. I have messed around with transforming the transform, but isn't doing what I think I should be getting (i.e. passing the result of one transform into the transform argument of the next seemed to just execute one completely and then animate the other).
This is easiest if you use a CATransformLayer as the parent of the image view's layer. So you'll need a custom view subclass that uses CATransformLayer as its layer. This is trivial:
#interface TransformView : UIView
#end
#implementation TransformView
+ (Class)layerClass {
return [CATransformLayer class];
}
#end
Put a TransformView in your nib, and add a UIImageView as a subview of the TransformView. Connect these views to outlets in your view controller called transformView and discView.
In your view controller, set the transform of transformView to apply perspective (by setting m34) and the X-axis tilt:
- (void)viewDidLoad
{
[super viewDidLoad];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1 / 500.0;
transform = CATransform3DRotate(transform, .85 * M_PI_2, 1, 0, 0);
self.transformView.layer.transform = transform;
}
Add an animation for key path transform.rotation.z to discView in viewWillAppear: and remove it in viewDidDisappear::
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0];
animation.toValue = [NSNumber numberWithFloat:2 * M_PI];
animation.duration = 1.0;
animation.repeatCount = HUGE_VALF;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[self.discView.layer addAnimation:animation forKey:#"transform.rotation.z"];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.discView.layer removeAllAnimations];
}
Result:
UPDATE
Here's a Swift playground demonstration:
import UIKit
import XCPlayground
class TransformView: UIView {
override class func layerClass() -> AnyClass {
return CATransformLayer.self
}
}
let view = UIView(frame: CGRectMake(0, 0, 300, 150))
view.backgroundColor = UIColor.groupTableViewBackgroundColor()
XCPlaygroundPage.currentPage.liveView = view
let transformView = TransformView(frame: view.bounds)
view.addSubview(transformView)
var transform = CATransform3DIdentity
transform.m34 = CGFloat(-1) / transformView.bounds.width
transform = CATransform3DRotate(transform, 0.85 * CGFloat(M_PI_2), 1, 0, 0)
transformView.layer.transform = transform
let image = UIImage(named: "3DgoldRecord")!
let imageView = UIImageView(image: image)
imageView.bounds = CGRectMake(0, 0, 200, 200)
imageView.center = CGPointMake(transformView.bounds.midX, transformView.bounds.midY)
transformView.addSubview(imageView)
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = 0
animation.toValue = 2 * M_PI
animation.duration = 1
animation.repeatCount = Float.infinity
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
imageView.layer.addAnimation(animation, forKey: animation.keyPath)
Copy the image from the question into the playground Resources folder and name it 3DgoldRecord.png. Result:
You may consider wrapping the recordLayer with a superlayer. Apply the x-axis rotation to the superlayer and add the z-axis rotation animation to recordLayer.