What is the best or recommended technique for animating PNG Sequences.
Heres what I've learned:
Do it Manually
Using MutableArrays containing Strings, you can animate a UIImageView with a timer which increments an index number
UIImage - animation methods
This works, the only problem is to find if an image has completed its animation, you have to check the isAnimating BOOL, and to do that you need a timer.
What is the best and recommended?
Looking to do Oldschool game sprite animations,
ie:
Idle Animation
Attack Animation
Walk Animation
ect...
Let me know if any one has something.
#lessfame
Example to animate arrows
UIImage* img1 = [UIImage imageNamed:#"fleche1.png"];
UIImage* img2 = [UIImage imageNamed:#"fleche2.png"];
NSArray *images = [NSArray arrayWithObjects:img1,img2, nil];
UIImageView* imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, 0.0, 160.0, 160.0)];
[imageView setAnimationImages:images];
[imageView setAnimationRepeatCount:100];
[imageView setAnimationDuration:3.0];
imageView.center = myView.center;
[imageView startAnimating];
[myView addSubview:imageView];
[imageView release];
Easiest way is to use Core Animation layers to do sprite animation:
Make a 'multi-cell' image strip (could be PNG or JPG) of all the various moves for the sprite. Make each 'cell' be a fixed height or width.
Put the image in a UIImageView.
Take the CALayer of the view, and adjust the contentsRect property of the layer. This acts as a clipping rectangle for the sprite. To animate sprite all you have to do is move the contentsRect over to the next cell position in the image strip.
Something like this will do it (assuming you've already calculated the newSpritePosition.
[CATransaction begin];
[CATransaction setDisableActions:YES];
spriteLayer.contentsRect = newSpritePosition;
[CATransaction commit];
(If you don't do the second line, then you'll get a default slide animation instead of a 'jump' to the next state.)
With this technique you can rotate through all the frames of the sprite -- much like a flipbook. CALAyer transactions are optimized so you should get pretty fast frame rates. You can set an animationDidStop completion handler to move to the next state of the sprite or loop back to the beginning frame.
It depends on how you are going to use the sprites. If you just need a simple looping sprite then the UIImage method is great. If you want more granular control then you will be happier loading the images into an array and cycling them using a NSTimer to handle the timing. Personally I use the array method most often because it leaves me more options in the future (like detecting when animations have completed). One more suggestion would be to check out the cocos2d project. Its sprite is alot more powerful than either of these suggestions.
As I said in another answer, I found this to be a good way: PNG Animation method by Moses DeJong
From his words:
This example implements an animation oriented view controller that simply waits to read the PNG image data for a frame until it is needed. Instead of alllocating many megabytes, this class run in about a half a meg of memory with about a 5-10% CPU utilization on a 2nd gen iPhone.
I'm still unsure exactly how it's done. I believe it basically uses UIImageView to cache up AVAudioPlayer.
Related
I started working on sample application, where I have some objects(images) which are animated on a UIView. I would like to have a background image which also should animate along with these objects.Two animations should happen at the same time. The background image occupies all the screen space and on the top of this the other images should be animated. Could someone guide me to achieve this.
I tried the following things mentioned in the link below.
for (int i = 0; i < IMAGE_COUNT; i++)
[_imageArray addObject:[UIImage imageNamed:[NSString stringWithFormat:#"D%d.png", i]]];
_animatedImages = [[UIImageView alloc]
initWithFrame:CGRectMake(
(SCREEN_WIDTH / 2) - (IMAGE_WIDTH / 2),
(SCREEN_HEIGHT / 2) - (IMAGE_HEIGHT / 2) + STATUS_BAR_HEIGHT,
IMAGE_WIDTH, IMAGE_HEIGHT)];
_animatedImages.animationImages = [NSArray arrayWithArray:_imageArray];
_animatedImages.animationDuration = 1.0;
_animatedImages.animationRepeatCount = -1;
The iPhone Game Background as Video or Animated Image?
But two animations are happening independently. I would like to animate the objects on the top of background image which also need to be animated.If this is not possible can I use layers or some other object to achieve this.Please suggest.
It seems like `UIImageView' provides no way to control the time the animation begins.
There is such an API, but it's in lower level CoreAnimation framework (which UIImageView must use in its implementation...)
You could re-implement you own UIImageView animation.
Then, to set different animations with relative timing, see "How can I chain Core Animations for different Layers one after the other?"
and How to group two CABasicAnimation animations and kick them off at the exact same time?.
As you don't need an offset between all your layers animations but want them to run at the same time, you should create a CAAnimationGroup, and put all of them with the same beginTime.
The CAMediaTiming protocol (implemented by CAAnimation) is pretty baldy documented - but this is the part you're interested in : timing your Core Animations - Check this post for some API clarification
I have a CALayer and as sublayer to this CALayer i have added an imageLayer which contains an image of resolution 276x183.
I added a UIPanGestureRecognizer to the main view and calculation the coordinates of the CALayer as follows:
- (void)panned:(UIPanGestureRecognizer *)sender{
subLayer.frame=CGRectMake([sender locationInView:self.view].x-138, [sender locationInView:self.view].y-92, 276, 183);
}
in viedDidLoad i have:
subLayer.backgroundColor=[UIColor whiteColor].CGColor;
subLayer.frame=CGRectMake(22, 33, 276, 183);
imageLayer.contents=(id)[UIImage imageNamed:#"A.jpg"].CGImage;
imageLayer.frame=subLayer.bounds;
imageLayer.masksToBounds=YES;
imageLayer.cornerRadius=15.0;
subLayer.shadowColor=[UIColor blackColor].CGColor;
subLayer.cornerRadius=15.0;
subLayer.shadowOpacity=0.8;
subLayer.shadowOffset=CGSizeMake(0, 3);
[subLayer addSublayer:imageLayer];
[self.view.layer addSublayer:subLayer];
It is giving desired output but a bit slow in the simulator. I have not yet tested it in Device. so my question is - Is it OK to move a CALayer containing an image??
Yes, it is OK to move a CALayer containing an image.
If all you want to do with the panning gesture is move the image, then instead of updating the whole frame, you should just update the layer's position property. Like thus:
- (void)panned:(UIPanGestureRecognizer *)sender {
subLayer.position=CGPointMake([sender locationInView:self.view].x, [sender locationInView:self.view].y);
}
Two things:
First, you can't draw ANY conclusions based on the performance of the simulator. Some things on the simulator are an order of magnitude faster than on a device, and other things are significantly slower. Animation is especially a mixed bag.
If you're doing performance-critical work, test it on the device, early and often.
Second, you can certainly animate a layer using a gesture recognizer, but that is an awfully round-about way to do it. Gesture recognizers are designed to work on views, and it's much easier and cleaner to tie the recognizer to a subview rather than a sub layer.
One of the big problems you will have with using a layer is hit-testing. If you let go of your image, then try to drag it some more, you'll have to have the gesture on the containing view, take the gesture coordinates and do hit testing on the layer. Ugh.
Take a look at the gesture based version of the touches sample app from Apple. It shows you how to cleanly move UIView objects around the screen using gestures.
Note that you can create a view that has custom layer content, and drag that around.
Anyone ever seen the problem of [UIColor initWithPatternImage] ignoring the alpha values of the PNG? If so, what's the best fix?
I am using a png as a background layer to my array of button objects and it contains several pre-set alpha values per pixel in the image that is to be used as a texture background. It loads fine as a pattern/texture-color, but it comes up with all key transparent area as opaque black.
It is important that I get the right alpha values so that the button images shows correctly. The button frames do not include the alpha shadows from the background as that is not the "clickable" portion of the button. Additionally, my button object's images and background images also makes use of transparency, so it really needs to have a clear background directly behind each button to let the proper true current color settings come through (lowest layer UIView will have its background color set to the current user's selected color). Setting just the single alpha value for the UIView layer containing this texture does not work for my needs either.
Any help would be appreciated. My current workaround would be to use fully-blown, repeatedly-programmed layout of several UIImageView using the png, instead of a single UIView with the pattern fill.
Here is a snippet of code, but it's pretty standard for turning a UIImage into a UIColor for use as a pattern/texture color:
UIView *selectorView = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,320)];
UIColor *background = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"SelectorViewBackground.png"]];
selectorView.backgroundColor = background;
[mainView addSubview:selectorView]; // pattern background layer. Add UIButtons on top of this selectorView layer
[self addSubview:mainView]; // current user selected color set in mainView.
[selectorView release];
[background release];
I had the same problem with setting a background on a UIView with some transparancy,
this is how I solved it:
theView.backgroundColor = [UIColor clearColor];
theView.layer.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:#"the_image_with_transparancy.png"]].CGColor;
This is probably related to:
Tiled Background Image: Can I do that easily with UIImageView?
Basically, try setting:
[view setOpaque:NO];
[[view layer] setOpaque:NO];
No I've never had an issue with this. I use code like the above all the time for apps (though often I use it in conjunction with a layer instead of a view but that shouldn't make a difference with transparency being recognized) and always have had transparency work fine.
I'd look into your PNG file. I've noticed iOS sometimes being finicky with certain PNG options/types (like an 8 bit PNG with 8 bit transparency). Make sure your PNG is saved as 24 bit with 8 bit transparency (32 bit total).
Also, stupid question, but have you verified there isn't anything black in the view/layer hierarchy behind your PNG? Sometimes it's the stupid things like that
For those who might need the work-around code where the background patterns can be laid out as rows in a UIScrollView, here it is (adjusted to remove confidentiality concerns, should work if variables properly set prior to call).
Note that there should be ways to reuse just the one allocated instance of UIImageView multiple times to either save memory or load times but time-to-market is my No. 1 driver right now. Enjoy the journey :)
UIImageView *selectorView;
for (int i = 0; i < numRows; ++i) {
selectorView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"SelectorViewBackground.png"]];
selectorView.frame = CGRectMake(0, i * patternHeight, patternWidth, patternHeight);
[mainView addSubview:selectorView];
[selectorView release];
}
In my iPhone app, I have put a UIBarBUtton of type UIBarButtonSystemItemTrash in my UIToolBar. When pressed, I'd like to replicate the animation of Mail.app: the bin opens, the UIView folds and flies into it.
Is there a way to access this animation ithrough the iPhone SDK?
Presently I am using a custom made animation, but there are some limits; for example, I cannot animate the bin itself.
Do you have any suggestion? Code samples?
Cheers,
Davide
Use the suckEffect type on an animation. Also: spewEffect, genieEffect, unGenieEffect, twist, tubey, swirl, cameraIris, cameraIrisHollowClose, cameraIrisHollowOpen, rippleEffect, charminUltra, zoomyIn, and zoomyOut. Doesn't work in the simulator.
CATransition *animation = [CATransition animation];
animation.type = #"suckEffect";
animation.duration = 2.0f;
animation.timingFunction = UIViewAnimationCurveEaseInOut;
view.opacity = 1.0f;
[view.layer addAnimation:animation forKey:#"transitionViewAnimation"];
Note: Code snippet was pulled from a larger codebase. I hope it works :)
Just to add some info:
You can use "suckEffect" with the standard +[UIView setAnimationTransition:forView:cache:]. Just pass the number 103 to the animationTransition variable. This won't avoid your app being rejected by Apple though :p
"spewEffect", "genieEffect", "unGenieEffect", etc. no longer exist on iPhoneOS 3.x. The only undocumented transition left are "cube" (--), "rippleEffect" (110), the three "cameraIris" effects (105,106,107) and "suckEffect" (103).
See http://www.iphonedevwiki.net/index.php?title=UIViewAnimationState for detail.
Also, to animate the bin (with private API): http://www.iphonedevwiki.net/index.php?title=UIToolbar#Animating_the_trash_can_icon.
Unfortunately, I think this is going to need to be an entirely custom animation. The UIView folding can be approximated using Core Animation, perhaps by adding perspective to the CATransform3D of the UIView's underlying layer to distort the UIView into a trapezoid which gets sucked into the trash can.
As far as the trash can, you can create a UIBarButtonItem using initWithCustomView:, which might let you insert a custom UIView that has an animatable trashcan. It looks like the trash can has two elements, the can base and the lid, which are rotated independently to open and close the can. Draw PNGs for both, make UIImageViews for them, and make them subviews of the UIBarButtonItem custom view. For opening and closing, apply rotational transforms to them to animate the subviews.
I'm not sure if this is an answer but here is lib that do "genie effect" so it's quite similar to what you want achieve.
CGRect endRect = CGRectMake(30, 40, 50, 60);
[view genieInTransitionWithDuration:0.7
destinationRect:endRect
destinationEdge:BCRectEdgeTop
completion:^{
NSLog(#"I'm done!");
}];
I have an animation that I'm displaying using a UIImageView:
imageView.animationImages = myImages;
imageView.animationDuration = 3;
[imageView startAnimating];
I know I can stop it using stopAnimating, but what I want is to be able to pause it. The reason is that when you call stop, none of your animation images are displayed, whereas I would like the last one that is up at the time when I hit a button to stay up.
I have tried setting the duration to a much larger number, but that causes all the images to disappear as well. There must be a really basic way to do this?
This code will pause an animated object at its current position in the animation process. If you record other variables like the time or progress or whatever you need, it should be fairly trivial to resume the animation again.
UIView *viewBeingAnimated = //your view that is being animated
viewBeingAnimated.frame = [[viewBeingAnimated.layer presentationLayer] frame];
[viewBeingAnimated.layer removeAllAnimations];
//when user unpauses, create new animation from current position.
Hmmm...since no one seems to know I guess it's not possible.
I went ahead and wrote my own UIView, with a UIImageView subview, that uses an NSTimer to switch between images. The advantage of this is that I can pause and resume the timer at my leisure, and performance doesn't seem to be an issue.
#oddmeter just a little edit:
animatedView.animationImages = images; //images is your array
[animatedView startAnimating];
//Then when you need to pause;
[animatedView stopAnimating]; //Important!!
animatedView.image = [images objectAtIndex: 0];
This should do the trick: https://developer.apple.com/library/ios/#qa/qa2009/qa1673.html
It basically tells you what you need to do to pause/resume any CALayer based animation.
If you feel uncomfortable using CALayer methods on UIImageView controlled animation, you could always just make the UIImage array based animation yourself. The code needed is very short, you can take it from here: http://rssv2.blogspot.com/2011/04/animating-series-of-images-with-calayer.html
Another option is to set the image property as well as the animationImages property. Doing this will display the static image when the UIImageView has its animations stopped.
Assuming your class is a subclass of UIImageView and have an NSMutableArray of images, do the following:
self.animationImages = images;
//yes, I'm skipping the step to check and make sure you have at least one
//element in your array
self.image = [images objectAtIndex: 0];
Maybe you can take a screenshot of the last animated image and display that?