Running and Moving Animation - core-animation

I have a sequence of images that I would like to animate. I am using the CAKeyframeAnimation class to run the animation. Here is the relevant part of the code:
[keyAnimation setValues:arrayOfImages];
[keyAnimation setCalculationMode:#"discrete"];
keyAnimation.fillMode = kCAFillModeForwards;
keyAnimation.removedOnCompletion = YES;
[keyAnimation setRepeatCount:2];
[keyAnimation setDuration:1.1];
[myLayer addAnimation:keyAnimation forKey:#"myanimation"];
This works fine. However, I need to also move this animation (sequence of images) from one point to another point (on the iphone screen).
I am able to move a static image using the the a CGMutablePathRef path reference. But I can't seem to move an animation which utilizes a sequence of images. How do I do this?
Thanks

Isn't this what the CAAnimationGroup is for? To make two animations on the same layer run at the same time?

Related

Take a screenshot of an UIView where its subviews are camera sessions

I'm building an app where I need to take a screenshot of a view whose subviews are camera sessions (AVFoundation sessions). I've tried this code:
CGRect rect = [self.containerView bounds];
UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
[self.containerView.layer renderInContext:context];
UIImage *capturedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Which effectively gets me an UIImage with the views, only that the camera sessions are black:
I've tried the private method UIGetScreenImage() and works perfectly, but as Apple doesn't allows this, I can't use it. I've also tried the one in Apple's docs but it's the same. I've tracked the problem to AVFoundation sessions using layers. How can I achieve this? The app has a container view with two views which are stopped camera sessions.
If using iOS 7, it's fairly simple and you could do something like this from a UIViewController:
UIView *snapshotView = [self.view snapshotViewAfterScreenUpdates:YES];
You can also use this link from a widow: iOS: what's the fastest, most performant way to make a screenshot programatically?
For iOS 6 and earlier, I could only find the following Apple Technical Q&A: [How do I take a screenshot of my app that contains both UIKit and Camera elements?]
Capture the contents of your camera view.
Draw that captured camera content yourself into the graphics context that you are rendering your UIKit elements. (Similar to what you did in your code)
I too am currently looking for a solution to this problem!
I am currently out at the moment so I can't test what I have found, but take a look at these links:
Screenshots-A Legal Way To Get Screenshots
seems like its on the right track - here is the
Example Project (and here is the initial post)
When I manage to get it to work I will definitely update this answer!

Optimize QuartzCore Animation with Shadows and Rasterization

Within my iOS app, I have a uiview that needs to be animated, transformed with gestures, shaded (using quartzcore shadows), and edited. When I perform animations and gestures on this UIView it is extremely "laggy". The animations aren't very "laggy" on the iPhone, however when using the iPad the animations become almost unresponsive (to the point where it seems like my app is crashing). I've tested my app using Instruments, and the app isn't taking up much memory / CPU / power until the animations begin. I have tested both on the device and on my Intel i7 8GB iMac and the animations are "laggy" on both.
The animation I am performing is nothing complex, it is simply a translation across the X Axis. After looking through every line of code related to the animation, I found that these lines are the issue(s):
viewer.layer.masksToBounds = NO;
viewer.layer.shadowOffset = CGSizeMake(-1, 1);
viewer.layer.shadowRadius = 3;
viewer.layer.shadowOpacity = 0.3;
The above code adds a shadow to the view that lags whenever I animate it (viewer). If I use the above code, but I add the following line animations work nicely:
viewer.layer.shouldRasterize = YES;
The problem with this code is that is seriously decreases the quality of the content displayed inside of the UIView (viewer). Here's an image with shouldRasterize set to YES:
Then the UIView without shouldRasterize:
Those screenshots are both from the same Retina iPad.
The ultimate question: How can I smoothly perform animations on a UIView with shadows (preferably using QuartzCore)? Is there a way to rasterize the content without degrading its quality?
The shadow properties on CALayer can be very inefficient while animating because it requires recalculating the shadow on every frame based on the contents of the layer. Luckily, the expensive part is the shadow path calculation, so if you just create a CGPath representing the shape of your content and assign it to layer.shadowPath then performance will skyrocket.
Since your layer seems to be completely filled with opaque data, the shadowPath is pretty simple:
layer.shadowPath = [UIBezierPath bezierPathWithRect:(CGRect){CGPointZero, layer.bounds.size}].CGPath;
The only downside is you'll need to edit this whenever the size of the layer changes.

Can´t get a smooth step by step rotation with CoreAnimation and CALayers

Im trying to rotate a CALayer with various sublayers according to time. There is UDP Multicast receiver in place which will receive new timecodes. I fetch new times via a timer:
[NSTimer scheduledTimerWithTimeInterval:0.02f
target:self
selector:#selector(fetchNewTimeAndRotate)
userInfo:nil
repeats:YES];
The CALayer should be rotated according to time. In my case, one rotation in 1.8 seconds. Actually i dont need any animation, i just need the angle to be set very often, so that it produces the actual animation.
I´ve tried setting the layers rotation in various ways:
1st i tried via CATransform3DMakeRotation:
CATransform3D rotation1 = CATransform3DMakeRotation([self DegreesToRadians:newAngle], 0.0f, 0.0f, 1.0f);
self.circleLayer.transform=rotation1
2nd try was using an instant rotation via an animation:
rotation.fromValue = [NSNumber numberWithFloat:[self DegreesToRadians:oldAngle]];
rotation.toValue = [NSNumber numberWithFloat:[self DegreesToRadians:newAngle]];
rotation.duration = 0.0f;
rotation.repeatCount = 0.0f;
rotation.removedOnCompletion = NO; //also used YES here with no effect
[self.circleLayer addAnimation:rotation forKey:#"transform.rotation.z"];
oldAngle=newAngle;
3rd try was just setting an angle to "transform.rotation.z"
[self.circleLayer setValue:[NSNumber numberWithFloat:[self DegreesToRadians:newAngle]] forKeyPath:#"transform.rotation.z"];
All of the above approaches do work, but lead to significant stuttering in the rotation. I´ve tried using several different timings in fetching and in animation-length. Nothing seems to get rid of the problem.
Using an autorotation every 0.02 and an 3.6° angle is the only option that presented smooth results for me. Am i making a fundamental mistake here or didn´t understand the concept of CoreAnimation? I know that it is to be used to make the animation itself. But i need it to react to user input and change immediatly.
Im thankful for any help.
You mean CADisplayLink, don't you?
DisplayLink objects are commonly used for frame-based animation in OpenGL, but they do not "use OpenGL". It's perfectly valid to create a display link and attach it to your application to trigger drawing.
As the other poster said, NSTimer is not a good choice for fine control of animation frames. If you write a program that just runs a timer and measures it, it will look good. However, timers depend on your app visiting the event loop frequently. They are not preemptive, and if your app is busy when the timer should fire, it will be off, or even miss a firing interval.
If you continue to use a display link, you might want to write your code to check the time elapsed from the last frame and calculate the amount to animated baed on that. That way if you drop a frame the animation still proceeds at the correct velocity (If you drop a frame then twice as much time elapses before the next frame, so you make your objects move twice as much.)

Animating retina images

I'm trying to animate some images. The images are working well on non-retina iPads but their retina counterparts are slow and the animations will not cycle through at the specified rate. The code i'm using is below with the method called every 1/25th second. This method appears to perform better than UIViewAnimations.
if (counter < 285) {
NSString *file = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"Animation HD1.2 png sequence/file_HD1.2_%d", counter] ofType:#"png"];
#autoreleasepool {
UIImage *someImage = [UIImage imageWithContentsOfFile:file];
falling.image = someImage;
}
counter ++;
} else {
NSLog(#"Timer invalidated");
[timer invalidate];
timer = nil;
counter = 1;
}
}
I realise there are a lot of images but the performance is the same for animations with less frames. Like i said, the non-retina animations work well. Each image above is about 90KB. Am i doing something wrong or is this simply a limitation of the iPad? To be honest, i find it hard to believe that it couldn't handle something like this when it can handle the likes of complex 3D games so i imagine i'm doing something wrong. Any help would be appreciated.
EDIT 1:
From the answers below, I have edited my code but to no avail. Executing the code below results in the device crashing.
in viewDidLoad
NSString *fileName;
myArray = [[NSMutableArray alloc] init];
for(int i = 1; i < 285; i++) {
fileName = [NSString stringWithFormat:#"Animation HD1.2 png sequence/HD1.2_%d.png", i];
[myArray addObject:[UIImage imageNamed:fileName]];
NSLog(#"Loaded image: %d", i);
}
falling.userInteractionEnabled = NO;
falling.animationImages = humptyArray;
falling.animationDuration = 11.3;
falling.animationRepeatCount = 1;
falling.contentMode = UIViewContentModeCenter;
the animation method
-(void) triggerAnimation {
[falling startAnimating];
}
First of all, animation performance on the retina iPad is notoriously choppy. That said, there are a few things you could do to make sure your getting the best performance for your animation (in no particular order).
Preloading the images - As some others have mentioned, your animation speed suffers when you have to wait for the reading of your image before you draw it. If you use UIImageView's animation properties this preloading will be taken care of automatically.
Using the right image type - Despite the advantage in file size, using JPEGs instead of PNGs will slow your animation down significantly. PNGs are less compressed and are easier for the system to decompress. Also, Apple has significantly optimized the iOS system for reading and drawing PNG images.
Reducing Blending - If at all possible, try and remove any transparency from your animation images. Make sure there is no alpha channel in your images even if it seems completely opaque. You can verify by opening the image in Preview and opening the inspector. By reducing or removing these transparent pixels, you eliminate extra rendering passes the system has to do when displaying the image. This can make a significant difference.
Using a GPU backed animation - Your current method of using a timer to animate the image is not recommended for optimal performance. By not using UIViewAnimation or CAAnimation you are forcing the CPU to do most of the animation work. Many of the animation techniques of Core Animation and UIViewAnimation are optimized and backed by OpenGL which using the GPU to process images and animate. Graphics processing is what the GPU is made for and by utilizing it you will maximize your animation performance.
Avoiding pixel misalignment - Make sure your animation images are at the right size on screen when displaying them. If you are stretching your image while animating or using an incorrect frame, the system has to do more work to process each frame. Also, using whole numbers for any frame or point values will keep from anti-aliasing when the system tries to position an image on a fractional pixel.
Be wary of shadows and rounded corners - CALayer has lots of easy ways to create shadows and rounded corners, but if you are moving these layers in animations, often times the system will redraw the layer in each frame of the animation. This is the case when specifying a shadow using the shadowOffset property (using UILabel's shadow properties will not render every frame). Also, borders and using maskToBounds and clipToBounds will be more performance intensive rather than just using an image editor to crop the actual asset.
There are a few things to notice here:
If "falling" is UIImageView, make sure it's content mode says something like "center" and not some sort of scaling (make sure your images fit it, of course).
Other than that, as #FogleBird said, test if your device have enough memory to preload all images, if not, try to at least preload the data by creating NSData objects with the image files.
Your use of #autorelease pool is not very useful, you end up creating an auto release object that does a single thing - remove a reference to an already retained object - no memory gain, but performance loss.
If anything, you should have wrapped the file name formatter code, and considering this method is called by an NSTimer, it is already wrapped in an autorelease pool.
just wanted to point out - when you are creating the NSString with the image name - what is the "Animation HD1.2 png sequence/HD1.2_%d.png" ?
It looks likey you are trying to put a path there, try just the image name - eg. "HD1.2_%d.png".

View behaviour differences between IOS 5.x and IOS 4.x

I've been trying to animate an image view to slide upwards on the screen. The views height is also increased whilst the position is moved upwards.
On my iPhone 4S (5.x) the image view behaves as expected, the view only moves upwards as its height is increased, however on my iPhone 3G (4.1), the view moves down a little bit during this animation.
Such a level of accuracy is needed as the image view is used to create a non expensive shadow effect. Its alignment is important for the effect. The image is a resizable graphic.
This is how I change the position and size of the view
CGRect oldShadow = self.shaddowView.frame;
oldShadow.size.height = oldShadow.size.height+200;
oldShadow.origin.y = oldShadow.origin.y - 200;
self.shaddowView.frame =oldShadow;
This is how the image for the view is set up as resizable:
UIImage* shadow = [[UIImage imageNamed:#"shadow.png"] stretchableImageWithLeftCapWidth:20 topCapHeight:20];
self.shaddowView.image = shadow;
Thanks.
I used the following animation, with a border around not only the starting position but also the intended final position, so that I could confirm whether any undesired shifting of the view (other than the obvious upward expansion) took place, but it worked fine on iOS 4.2.1:
[UIView animateWithDuration:2.0
animations:^{
CGRect newImageFrame = imageView.frame;
newImageFrame.origin.y -= stretchBy;
newImageFrame.size.height += stretchBy;
imageView.frame = newImageFrame;
}];
I don't have iOS 4.1 device sitting around (my old 3G test phone is running iOS 4.2.1, the latest supported iOS version for that device), so I can't speak to that, but it's fine with iOS 4.2.1.
I have to confess, though, that I find it very unlikely that when you animate the changing of a frame, that the final frame would not be precisely what you specified it to be. If you NSLog the frame when you're done, it is not the correct value? Or are you saying that it momentarily moves during the animation but then ends up in the correct location? Or that it shifts down during animation and ends up in the wrong position even when the animation is done?
I wonder if there's something else going on (e.g. is your animated view a subview of a scroll view, which itself might be shifting? or is there some code not shown here that is accidentally further adjusting the frame after the animation? etc.). Seems like a little debugging should confirm whether the frame is actually not what you intended, or whether there is some other issue going on.
I originally answered suggesting shouldRasterize option, but if you're trying to support old iPhone 3G devices, then maybe that's not good enough. Definitely stutters a little on these old phones. Anyway, this was my original answer:
I assume you're doing this because the layer shadow feature is a little CPU intensive. But I've heard (but can't speak to it) that if you use the shouldRasterize option, it's a little better:
viewThatNeedsShadow.layer.shadowColor = [UIColor blackColor].CGColor;
viewThatNeedsShadow.layer.shadowOffset = CGSizeMake(3.0, 3.0);
viewThatNeedsShadow.layer.shadowOpacity = 0.5;
viewThatNeedsShadow.layer.shouldRasterize = YES;
viewThatNeedsShadow.clipsToBounds = NO;