I want to make an animated background for iphone app. Something simple 5-6 frames changing in the loop. On the front there will be another animation running. How can this be done?
The easiest thing to do is probably to use the animationImages property of a UIImageView. Once you have the animationImages property correctly set up, just call startAnimating on your view. So your code would look something like:
imageView.animationImages = myNSArrayofUIImagesObjects;
imageView.animationDuration = 1; // by default this is equal to the number of images multiplied by 1/30th of a second
[imageView startAnimating];
An important thing to note is that you can't easily control how long each image is shown. But what you can do is use the same image in your NSArray of images multiple times. So, for example, you could have an NSArray of length 500, where the first 100 entries map to your first image, the second 100 entries map to your second image, etc. Make sure to minimize the amount of memory you're loading onto the heap by reusing the same UIImage object for each of your five or six images.
Related
I am a little confused about the behavior of animationImages in UIImageView. The documentation says:
Setting this property to a value other than nil hides the image
represented by the image property.
Yet, if I initWithImage, then set animationImages property, the image passed to the init call is displayed. When I start animation, it goes through the array as expected, but once animation is done, it reverts back to image. Is the documentation inaccurate?
What I want ultimately is for the UIImageView to display the first image in the array, then once animation is complete, display the last image in the array. From what I can tell, it seems like I'll need to set image manually at the beginning to the first frame, then set it to the last frame right before I start animation?
you can make the start img as the img you want to start with, and when the animation is running you change the uiimage to the last img you want as the last img, so when the animation stops the last img will be showing.
Yes, set the image property to the image you want to see as the first image. Just before you call startAnimation, change that to be the last image so after the animation stops it rests on the last image:
In my setup:
animatedIV.animationImages = imageArray;
animatedIV.image = [imageArray objectAtIndex:0];
animatedIV.animationRepeatCount = 1;
before I start animation:
animatedIV.image = [imageArray lastObject];
[animatedIV startAnimation];
I've got an array with several images that I'm adding into an UIImageView:
myImageView.animationImages = myArrayOfImages;
Now, I want to execute that loop of images X times, but each one with a different duration
myImageView.animationDuration = randomTime;
Is this possible?
Have you tried? I believe you will need to create a new instance of the UIImageView each time.
I have a question regarding a png, animated image sequence.
I am loading images from 1 - 35 into a layer and I am initializing the layer with the 1st image using - initWithImage. I have several buttons that want to be able to play different ranges of the image sequence array.
Is there a way to play only the images from 1 - 10?
Is there a way to control the range of images played... 11 - 35 or 4 - 20 or whatever?
Of course I tried creating separate layers with separate array's of images and having the layers on top of each other. The problem their is the initWithImage. If I play the second layer I can see the initialized image underneath.
Here is my image sequence code:
- (void) loadAnim05 {
cAnim = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"cof_01.png"]];
cAnim.center = CGPointMake(78,211);
NSMutableArray *array = [NSMutableArray array];
for (int i = 1; i <= 35; i++)
[array addObject:[UIImage imageNamed:[NSString stringWithFormat:#"cof_%02d.png",i]]];
cAnim.animationImages = array;
cAnim.animationDuration = 1.0;
cAnim.animationRepeatCount = 1;
[self.view addSubview:cAnim];
[cAnim release];
}
Thank you very much for any help.
Why don't you maintain an array of images elsewhere and use -subarrayWithRange: method of the NSArray object to pick the images that you want to animate and assign it to the animationImages property of your imageView.
But I don't think UIImageView provides you with mechanism to play a subarray of animationImages.
I am in the process of writing a little animation framework to do exactly this. You can find the code at http://github.com/st3fan/iphone-animation
Key features are:
Optimized for PNG images with not too many shapes on a plain background
Includes a tool to convert a PNG sequence (like exported from Flash) to a simple one-file container format
Keeps frames compressed in memory. Decompresses realtime.
Frames are compressed with a simple run-length encoding
Typical full screen animation takes ~ 10% CPU
I wrote this specifically for games in the http://tickletapapps.com collection. It works very well for the kind of animations used in those games. Contact me via Github if you need help. The code is work in progress but working pretty well.
My data visualization app incurs a large memory consumption spike during redraw (setNeedsDisplay which triggers drawRect). I am currently redrawing the entire view that houses the data plot. This view is much larger then the device display.
Is there any way to tell CoreGraphics to allocate just enough memory to draw each element (each element is a small rectangular block much smaller then the device display) and release the memory when done, rather then my current naive approach?
Thanks in advance.
-Doug
UPDATE 8 Dec 8:28am EST
Here is the relevant code with explanatory wordage. I am running Instruments with ObjectAlloc, Memory Monitor, and Leaks instruments running. The only memory leak I have is due has to do with the NSOperationQueue not releasing mems. This is minor an not relevant.
Architecturally the app consists of a tableView with a list of interesting locations along the human genome to inspect. When a table row is selected I enqueue a data gathering operation that returns data called alignmentData. This data is then plotted as horizontal rectangular slabs.
Initially, when the tableView launches my memory footprint is 5 MB.
- (void)viewWillAppear:(BOOL)animated {
// Initial dimensions for the alignment view are set here. These
// dimensions were roughed out in IB.
frame = self.alignmentView.frame;
frame.origin.x = 0.0;
frame.origin.y = 0.0;
frame.size.width = self.scrollView.contentSize.width;
frame.size.height = 2.0 * (self.containerView.frame.size.height);
}
Note: After viewWillAppear: is called the memory footprint has not budged. Even though the alignmentView is be sized well beyond the dimensions of the display.
This is the method called from the data gathering operation.
- (void)didFinishRetrievingAlignmentData:(NSDictionary *)results {
// Data retrieved from the data server via the data gathering operation
NSMutableData *alignmentData = [[results objectForKey:#"alignmentData"] retain];
NSMutableArray *alignments = [[NSMutableArray alloc] init];
while (offset < [alignmentData length]) {
// ...
// Ingest alignmentData in alignments array
// ...
} // while (offset < [alignmentData length])
[alignmentData release];
// Take the array of alignment objects and position them in screen space
// so that they pack densely creating horizontal rows of alignment objects
// in the process.
self.alignmentView.packedAlignmentRows =
[Alignment packAlignments:alignments basepairStart:self.startBasepairValue basepairEnd:self.endBasepairValue];
[alignments release];
[self.alignmentView setNeedsDisplay];
}
After this line of code:
self.alignmentView.packedAlignmentRows = ...
The memory footprint is 13.8 MB
After this line of code:
[self.alignmentView setNeedsDisplay];
The memory footprint spikes to 21.5 MB, stays there for a few seconds then returns to the pre-existing level of 13.8 MB
The solution I am looking for would allow me to essentially, create a horizontal render buffer window that that is the height of a single row of alignment objects. I would allocate its memory render into it, then discard it. I would do this over and over again for each row of alignment data.
In theory, I could render an infinite amount of data with this approach which of course would be most excellent ;-).
-Doug
Here is the - not so obvious answer to my memory problem. I'll give myself this one because I learned it on the Apple dev forum form Rincewind - a very helpful Apple engineer BTW.
It turns out that by slicing a large view into N smaller pieces and rendering into each in turn I will incur a memory spike that is roughly 1/N the size of the large view.
So, for each smaller view: alloc/init, feed a portion of my data, setNeedsDisplay. Rinse/repeat for all N small views.
Simple, eh?
Prior to learning this I had mistakenly thought that setNeedsDisplay:myRect did this for the large view. Apparently not.
Thanks for all the suggestions gang.
Cheers,
Doug
#dugla
"This view is much larger then the device display."
So you're scrolling through the data representation by moving the view around? You might want to consider making your view the same size as the display and using CGTranslate to adjust the drawing offset within your drawRect function. It sounds like you're drawing tons of stuff, and CoreGraphics can't tell what's visible and what is not.
You'll get much better drawing performance if you make the view smaller and insert checks to avoid drawing things that are outside the view's bounds.
This is very possible, you will need to specify which sections of the screen need to be drawn, you need to call setNeedsDisplayInRect as described here and pass in a CGRect which is the area you wish to be redrawn.
This is much, much faster than re-drawing the entire screen, I had issues with this in an iPhone drawing application I created a year and a half ago.
In addition to Ben's suggestion:
If you're scrolling around your data, consider adding a few smaller views to the scrollview. This way you don't need to redraw most of the time, but only when some area of your scrollview isn't covered any more. Basically, if one of your subviews scrolls completely out of sight you'd move it to the opposite side of the visible area and redraw it accordingly.
In my app I'm only scrolling horizontally, and am using two subviews. Let's say view1 is on the left and view2 on the right. When view2 scrolls out of sight, I move it to the left of view1 and redraw it accordingly. If the user scrolls further in the same direction view1 will scroll out of sight as well and I'll move it to the left of view2 and so on.
If you need to scroll horizontally and vertically you'd need 4 views.
I know you are probably aware of this, but have you looked at the Core Plot framework for your data visualization? We recently added touch-scrolling of graphs and we've tried to be conservative when it comes to memory within the framework. Without knowing more about your specific case, this might be something you could try.
I have a UIImageView object that is just a plain black rectangle.
This is what i use to select a button in the view.
Problem is, I have 49 of these buttons in my view, and all of them can be selected at the same time.
What I use for adding a subview to a button is:
UIImageView* selectedSquareView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,40,40)];
[selectedSquareView setImage:[UIImage imageNamed:#"SelectedSquare.png"]];
[button addSubview: selectedSquareView];
I would like the selectedSquareView to be reused multiple times as subviews for the other buttons, but only keep one allocation of it. I would prefer not having 49 UIImageViews created at the same time just for this purpose. Is this possible?
If not, should I store them in a NSMutableArray for easy removal later?
Regards
-Raymond
You will need 49 UIImageViews, you only need 1 UIImage. The UIImageViews contain position, size, is_higlighted, etc information for each button.
That being said even if you had a lot of UIImage's, UIImage is supposed to be pretty intelligent about these things as Apple describes in their documentation:
In low-memory situations, image data may be purged from a UIImage object to free up memory on the system. This purging behavior affects only the image data stored internally by the UIImage object and not the object itself. When you attempt to draw an image whose data has been purged, the image object automatically reloads the data from its original file. This extra load step, however, may incur a small performance penalty.
You should avoid creating UIImage objects that are greater than 1024 x 1024 in size. Besides the large amount of memory such an image would consume, you may run into problems when using the image as a texture in OpenGL ES or when drawing the image to a view or layer. This size restriction does not apply if you are performing code-based manipulations, such as resizing an image larger than 1024 x 1024 pixels by drawing it to a bitmap-backed graphics context. In fact, you may need to resize an image in this manner (or break it into several smaller images) in order to draw it to one of your views.
Alternatively if you really feel like you need to do delete the UIImageView's when not in use you can do as you suggest store them in an array and release them on viewDidDisappear and then recreate them all on viewWillAppear.
Each UIView appears only once, so you will definitely need to create 49 copies of it.
Your current code is probably fine, since UIImage will probably cache the image, but you might like to create the image only once and then set it each time, something like:
UIImageView* selectedSquareView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,40,40)];
static UIImage* kSelectedSquareImage = [UIImage imageNamed:#"SelectedSquare.png"] retain];
[selectedSquareView setImage:kSelectedSquareImage];
If not, should I store them in a
NSMutableArray for easy removal later?
It depends if there are any other views in the container view - if not, then there is no need to store them in an NSMutableArray as you can just use container.subviews to get an array of views. Otherwise, sure, you could store them in an NSMutableArray and remove them that way (just make sure you remove them from the array or release the array as well, otehrwise they will remain in memory simply because they are stored in the array).