13
Jan
09

imageNamed is evil

So a little while back we wrote about the confusing crashes on the device that we eventually figured out were caused by [UIImage imageNamed:] running out of memory when it tried to cache lots of big images. So we apparently solved the problem by making a thumbnail-sized image set and caching those, some 2.9 meg worth, whilst loading in the full sized ones uncached and only as needed.

Well, apparently turned out to be not good enough. See, it’s a few days later and we’ve finalized the design and got everything implemented and all seems to be good … except that it gets terminated without warning sooner or later. And quite often, Springboard terminates as well. Absolutely no correlation to any action or sequence in the program, no leaks, no out of bounds memory accesses, memory usage of the program barely a pittance. Yet, somehow, look at the console and you see system memory warnings scrolling by, you see it quitting background processes, and eventually quitting Springboard and/or the active application. Whilst that active application hums merrily along in blithe ignorance of the system crashing to the ground behind its back.

So apparently not only is 2.9 meg of images cached with +imageNamed enough to bring the iPhone OS to its knees, it’s not smart enough to, you know, actually do anything about it, like oh I don’t know, empty the cache or something?

It’s not like this is hard or anything, you can replicate the caching functionality precisely by declaring yourself an  NSMutableDictionary *thumbnailCache and populating it like

- (UIImage*)thumbnailImage:(NSString*)fileName
{
   UIImage *thumbnail = [thumbnailCache objectForKey:fileName];

   if (nil == thumbnail)
   {
      NSString *thumbnailFile = [NSString stringWithFormat:@"%@/thumbnails/%@.jpg", [[NSBundle mainBundle] resourcePath], fileName];
      thumbnail = [UIImage imageWithContentsOfFile:thumbnailFile];
      [thumbnailCache setObject:thumbnail forKey:fileName];
   }
   return thumbnail;
}

and if you do get a low memory issue, just [thumbnailCache removeAllObjects] and you’re good. But you know what? After replacing the various +imageNamed calls with this … not a single quibble anywhere, the whole 2.9 meg worth of cached thumbnails go right in there and not a single problem for, literally, hours.

(Not that there were any problems after those hours, we hasten to add; simply that, you know, if you think about it, there probably really isn’t a lot of point extending testing of an iPhone application much past the lifespan of a full battery charge…)

So the moral of the story is: DO NOT USE [UIImage imageNamed] for any significant amount of images. It is EVIL. It WILL bring down your application and/or Springboard, even when your application is putting along using just barely a nibble of memory on its own. Take the handful of lines above and implement your own cache!


79 Responses to “imageNamed is evil”


  1. 1 IphoneWorker Jan 21st, 2009 at 1:24 am

    Problem with imageWithContentsOfFile is that it seems to be 2-3 times slower than imageNamed. (loading in 80 or so thumbnails) 1.7 seconds is now over 5 seconds on the device.

    What OS are you running. Apple claims 2.2 is supposed to be better for cache releasing.

  2. 2 Alex Jan 21st, 2009 at 8:08 am

    Really? I load in 368 thumbnails max as the user scrolls, and if it is slower it’s imperceptibly so. But maybe that’s just a usage artifact. Perhaps you could try threading your image loading then?

    And no, all my observations that imageNamed will kill Mail and kill Springboard and kill your app without ever releasing images or asking you to do anything are all with 2.2. I stumbled across some discussion that it is believed that this is designed behaviour on imageNamed’s part — that it’s meant only for caching images that Absolutely Must be available to present a functional interface, and crashing is preferable to releasing anything loaded that way.

  3. 3 Joe Feb 2nd, 2009 at 11:08 pm

    I ran into the exact same image while loading up background images (for use in animation) while working on the “Fairies Fly” game for Disney. I tried to run down “imageNamed” with some bugs and background loading as well, which is where I first ran into the incredible amount of caching that happens under the covers with it.

    The download of loading from file yourself is that your device can take a bit to load a large number of images, meaning you need to carefully deal with all that in the background to avoid the “apparently frozen” issue that you get otherwise. Not insurmountable, but damned annoying anyway.

    I never did find a good lazy-load ready setup for image use – ended up (like you) dealing with caching and loading all on my own.

  4. 4 Ari Braginsky Mar 2nd, 2009 at 4:07 pm

    I too ran into this issue and ended up manually caching things as well.

  5. 5 David Casseres Mar 17th, 2009 at 3:40 pm

    Thank you, thank you, thank you! With a release deadline very close, QA began telling me about random, irreproducible crashes when they simply banged on my UI continuously. They told me flatly “It will crash. Sometimes in ten minutes, sometimes in an hour.”

    So I looked at the logs, and they all ended with an exit status of 101. And my didReceiveMemoryWarning methods were logging. It had been awhile since I googled “exit status 101,” so I did, and found your article pretty quickly. And I said Well, look at that.

    Yesterday I implemented your solution throughout my code, and today QA sent an email saying they had been flogging the UI for six solid hours without a hiccup!

    Did I say thank you already? Thank you.

  6. 6 Alex Mar 17th, 2009 at 6:21 pm

    You’re very welcome, David, glad I could be of help, and good luck with your project!

  7. 7 John Griffiths Apr 9th, 2009 at 1:41 am

    Thanks very much for this Alex! I’m wrapping up development of my first app and have been tearing my hair out trying to find out why my relatively simple and trivial app has been crashing so much and running into memory problems constantly. And, guess what? It turned out to be all thanks to imageNamed!! I found that replacing all imageNamed calls with initWithContentsOfFile caused by table views to grind to a snails pace but upon implementing a version of your caching code above, all my problems have been solved!! Not only is it now stable as a beast but overall performance has improved massively as well!

  8. 8 Alex Apr 9th, 2009 at 6:34 am

    And you’re most welcome too John. I’m glad to see all you people being saved my trouble sussing this out!

  9. 9 Marc Charbonneau Apr 16th, 2009 at 12:36 pm

    I’d argue against avoiding it altogether, I think it really depends what you’re planning on using it for. As long as you’re aware the method is caching images you should be able to make an educated decision about a few 16×16px icons that are always going to be displayed, versus a 800×600px picture the user will only see once.

    Implementing your own cache you can empty when you need to is a good idea though, I’ll have to keep that in mind.

  10. 10 Anonymous May 4th, 2009 at 10:59 pm

    I hate to ask the obvious, but any updates to the imageNamed fiasco for 3.0?

  11. 11 Lance Weber May 10th, 2009 at 7:36 pm

    Alex – thanks a ton for this post, you saved me some serious heartache trying to track down my app crashes. One of the things I’m going to play with next week is combining the caching idea here with some of the url based background loading ideas I’ve seen out there. If it’s done right we should be able to have seamless large image loading/caching.

  12. 12 Paul May 15th, 2009 at 9:39 am

    Hello,
    Has anyone got this working with a UIImageView for animating a large number of
    large images (say 200px by 120 px?)? I’ve implemented the imageCache as a way
    of trying to fix a UIImageView animations problem. However when I run it the
    3G iPhone, the device still becomes unresponsive. I can see the appDelegate
    getting the memory warning, and clearing the imageCache.
    I used it like so:

    NSMutableArray *arrayOfImages = [[NSMutableArray alloc] initWithCapacity:53];
    for (int i=1;i<53;i )
    {
    NSString *filename = [[NSString alloc] initWithFormat:@”introd.png”,i];
    // Retrieve the image
    UIImage *savedImage = [[ImageCache sharedImageCache] imageForKey:filename];
    // Add image to array
    [arrayOfImages addObject:savedImage];
    }

    self.myImageView.animationImages=arrayOfImages;
    [self.myImageView startAnimating]

  13. 13 Alex May 15th, 2009 at 9:57 am

    @Paul:

    That’s on the order of 100K per image. Couple dozen of those and you’re going to start running into iffy memory territory, your own cache or not. Instruments would be of use in detecting what’s actually going on.

  14. 14 Roger Nolan May 29th, 2009 at 12:13 am

    There seems to be a lot of FUD around imageNamed. Does anyone have any real knowledge of perfomrance particularly under low memory conditions?

    I’ve created a SO question for any answers.
    r

  15. 15 Daniel Alexander Jun 1st, 2009 at 7:35 pm

    There will be two groups of people that find this web page. Those that are just loading images for static display purposes and those that are loading images for purposes of animation (or should I say that it will be people with problems doing one of these two things).

    For those that are simply loading images for static display purposes this might help you to manage your memory more effectively. However, this is NOT because this code is caching anything because it isn’t (at least not in the way imageNamed is), but simply because it is not using imageNamed (simply because it is NOT caching is why it works). The same effort can be accomplished by simply using imageWithContentsOfFile (or any number of other methods) and tossing the images into an NSMutableArray (or better yet storing the CGImageRef in an array). The point is that this code is NOT a substitute for the caching that you get when you use imageNamed. It simply avoids using the caching at all and as a result you don’t get the memory problems (but you could get other problems…see below). So, for those that are simply displaying static images the advice is simple. Don’t use imageNamed.

    Now, for the rest of us (and I would say the majority of people that read this webpage). This code will not help you at all with the animation. It is not really caching anything (at least not the way imageNamed does it). If you are like most people you are animating just fine with imageNamed, but after a while you get a memory error because imageNamed is caching all your images even if you disposed of them (they still sit there in memory). So, you search the web and you find all sorts of advice that tells you not to use imageNamed. So you do that only to find that your animation crawls when you use something else. Although you have gotten rid of your memory error by avoiding the caching you have introduced a bigger problem by slowing down your animation to a crawl. The above code is no different. The above code is simply using a dictionary instead of an array to store the images, but it is the same thing (nothing is really cached). So, I am afraid to say that this solution will not help anyone that is having trouble animating their images.

    I should mention that this is not a problem if you are simply animating a few images or small sized images. So long as you can store all the images in memory then you won’t run into this problem. Its just that at some point you might have too many images or your images are too large (it doesn’t take much). At that point using imageNamed is not going to work, but then what else can you do but use imageNamed if you are animating?

    And here is the sad part. I got your hopes up, but I don’t have a solution. I haven’t tried to tackle such a problem yet. Maybe I will mess with it a bit and see what I can come up with.

    Still, I suspect that you might have to move to using OpenGL or something, but I am not yet willing to give up on some simple solution. I was thinking that you might be able to load the images onto CGLayers (or UIView’s/UIImageView’s if you don’t want to mess with CGLayers) and then simply bring the layers into view to do your animation.

    Since you would not be using imageNamed, but rather use something else (mentioned above) then you will avoid the memory problem. Also, since you are both loading and displaying the images during the load of all your animation frames you should also be able to avoid the animation crawl problem.

    Of course, this will likely slow down your animation images overall load time. But that is probably a small price to pay to be able to avoid both the memory problem and the animation crawl problem. You would likely have to add a progress bar (by the way, you will shot yourself trying to add a progress bar…hint…use multithreading).

    Anyway, I will try to throw together some test code for the CGLayers idea and let you know if it works. Don’t really have the time right now though. If someone else gets it working please let us know.

  16. 16 Daniel Alexander Jun 1st, 2009 at 8:37 pm

    Paul,

    Let me list off a few pitfalls/ideas/thoughts.

    1. Figure out what your seconds per frame is. You didn’t list your animation duration so I can’t figure it out (in your case it is 53/duration). That value will give you some idea of how long you have to display each frame. If you take longer to display a frame than the time you have given yourself to display a frame then you have a different problem altogether (or I should say you have an additional problem to the ones I have already mentioned). The timer messages will start to back up and the OS will shut you down and/or your animation will be slow and/or choppy.

    2. Do not resize your images. If you are loading an image that is 300×300 then put it into a CGRect that is 300×300; otherwise, your animation will slow down to a crawl. It is possible that if you are currently resizing your images that you can solve your problem by simply not resizing them. Though I suspect that such is not the case given the sizes of your images. Still, you will have no hope of solving the problem if you are resizing your images (intentionally or unintentionally). So, do not resize your images.

    3. Scrap the animationImages approach altogether and instead create a timer that triggers the displaying of each frame. Actually you display the frame in your drawRect, but in the timer you will increment some current frame variable (an index into your images array) and do a setNeedsDisplay to cause drawRect to fire (so the timer will only have a few lines of code). The drawRect will then simply draw the image that is indexed with that current image variable (some int). As I understand it, animationImages has some overhead that doesn’t allow you to animate frames as fast as you can do if you do it yourself; however, that is not why I suggest you use a timer. It is likely that you cannot solve this problem if you are not able to write the animation code for yourself. For example, if the solution is to use OpenGL or to swap between CGLayers (or UIImageViews) then you must use a timer in order to do such.

    4. Use imageNamed until you know that you cannot use it. This is to say that if it is possible for you to use imageNamed without running into the memory problem then that will be your best chance at animating your images fast (because they are all cached). In such a case, if your animation is slow then it is likely due to some other reason (like you are resizing or something). You can speed things up by manipulating your PNG files in Photoshop. In the end, if you are not having a memory problem and yet you are still having a problem with slow animation then you are not giving each frame enough time to display. You have to cut. Cut out frames, cut down the image dimensions, cut out colors, cut cut cut. Or you can increase your animation duration, but you have to do something to get your “frame display time” down below your “seconds per frame time”. Of course, if you have a memory problem then you can’t use imageNamed and thus have a different problem altogether.

    5. I suspect that some settings in your PNG files slow things down a bit. Like I think interlacing slows things down, but there might be other PNG properties that should be considered (use something like Photoshop to manipulate your PNG’s).

    6. You might want to use CGImageRef instead of an array of UIImage’s, but it really won’t have much effect on the animation times. So, either way is fine. Though I think the dictionary idea kinda confuses things (particularly if you name it with the word “cache”). It’s probably easiest just to use an array (mutable) or even a static array of CGImageRef’s (like CGImageRef myFrames[50];). However, it’s all the same stuff really. If you are comfortable using a dictionary then that is fine.

    7. In the end you will likely need to come up with some more creative way to push the displaying of your images into your loading time. But you gotta do it in some way that doesn’t make use of the same caching that imageNamed does (this is what I fear will happen even if we use CGLayers or UIImageViews to display all the images during our load time–it might still cache the images and then the memory problem will occur again).

    Hope this is at least a bit helpful. You can search for code that does most of this stuff online (like animating using an timer) so I won’t post any here until I actually get something working for myself.

  17. 17 Daniel Alexander Jun 1st, 2009 at 9:12 pm

    Ahhhh…I made a number of typos in those two messages. Sorry for the typos, but in particular I wanted to point out that it should have read “duration/53″ for your seconds per frame calculation.

    For example, if I was to guess I would say that a 200×120 image would average about 0.008 seconds to display (provided it is loaded with imageNamed–this is just an educated guess). However, I’d say that that display time could get up to as much as 0.03 or maybe higher (it varies due to what is going on on the iPhone at any given moment). Again, these are just educated guesses so I can illustrate my point (you can measure these actual times if you use drawRect and a Timer, I would probably do so just to be sure of what the limits of my images are).

    Anyway, if your animation duration is 1 second then you would have only 0.018 seconds to display any given frame (given that you have 53 frames to display). I think that would be a problem if a number of your frames fall above 0.018 seconds to display. If that is the case then you would need to do something to give each frame more time (like reduce the number of frames or increase the animation duration).

    Mind you, this is not normally a problem since the fps of NTSC TV is about 23 fps and looks good enough. That means that you really only need to animate at 23 fps giving you 0.043 seconds per frame as a lower limit. There is plenty of elbow room in that time. So, if need be you can reduce the number of frames you are animating so long as you don’t go below 23 per second (though you might want to use the PAL standard of about 29 fps–or something in between).

    Of course I am only guessing about your animation duration (you did not tell me), but whatever it is you should be thinking about it as I have just done here. What is your fps? What is your seconds per frame? How long do you suspect it should take to display a UIImage on the screen? Does all your figures work out correctly?

    Once you get your Timer added to your code you can then test out how long it actually takes to display each of your images. Then so long as you can keep above those times you should be able to animate your images properly.

    Note, that I am ignoring the memory problem in this illustration. However, even if you end up having a memory problem and cannot use imageNamed you still would need to consider all these timings (fps, etc) in whatever solution you eventually ended up with. So, if you have a memory problem then you can’t use imageNamed and instead you use CGLayers or something. Well, you still need to be able to make sure you can switch between layers faster than the time you have given for each frame to be displayed (seconds per frame). So all of this stuff still comes into play (it still needs to be considered no matter what approach you use).

  18. 18 Daniel Alexander Jun 2nd, 2009 at 9:03 am

    Opps, lots of typos. CGLayers should obviously be CALayers. There is too many for me to fix. Most of the typos are easy enough to figure out what I meant. Sorry about that.

  19. 19 Daniel Alexander Jun 2nd, 2009 at 9:17 pm

    Ok, using CALayers and UIImageViews is still slow at displaying (animation is still slow). I tried swapping layers, manipulating the Hidden property to display the current layer, and playing around with the zPosition property to bring forward the current layer, but it still slows down the animation (playing with zPosition was the most promising). Nothing gets close to the speed of using imageNamed (at least not yet…I still a few more things for me to try).

  20. 20 Daniel Alexander Jun 2nd, 2009 at 9:28 pm

    Paul,

    For my tests I generated sets of 40 images of sizes 300×300 (about 10 sets). I found that the code doesn’t really mess up until I have loaded more than 4 sets. Loading one set was never a problem. So, I suspect that if you only have one set of 53 images of size 200×120 you should probably not be having any problem even using imageNamed. This means that your problem is most likely with the way you are displaying the images. It probably means that your duration is too fast for the number of frames you are attempting to animate or that you are resizing your images during the animation (intentionally or unintentionally).

    Also, as I said, using animationImages might have some additional overhead that would limit how fast your duration can be. You should probably use a timer as I suggested.

    In any case, let me know your Duration of your animation and I can tell you if that is your problem or not.

  21. 21 Daniel Alexander Jun 3rd, 2009 at 8:24 am

    So far I think the conclusion is as follows:

    Use imageNamed if you are not running into any memory problems. Provided you are not doing anything massively stupid like creating huge images or images with massive colors then you should be OK using imageNamed for most projects. In my example I used multiple sets of 40 images of size 300×300. My example loaded only 40 images at a time (properly coding the disposal of the previous 40), but since I was using imageNamed it was likely that the previously loaded images were not removed from memory (the source of all this “imageNamed is Evil” thinking).

    Doing this I was able to load (and dispose) of a number of sets before I ran into any memory issues. From this you can get some idea of what the capabilities are for imageNamed. If you just loading 50 images for an animation then you should NOT have a problem (Paul would likely fall into this category, his problem is likely related to something else–which should be good news for Paul), but if you are loading sets of 50 images and you hope to get 10 or 20 or even 30 sets into your application (or if you wanted to load hundreds of images for a single animation) then you will likely have a problem (if you use imageNamed). In the latter case you would NOT be able to use imageNamed, but in most cases using imageNamed is the way to go (mainly because I don’t yet know of another solution for animating without it…at least not yet…I do have an idea that might just work without using OpenGL).

    And don’t assume that because you are running into memory problems that it is a problem with imageNamed. It is more likely a problem with your disposal of your temporary objects within the loop that loads your frame images. Using imageNamed may result in a memory problem, but that doesn’t mean it is the culprit in ever instance. For example, Paul is loading 53 images of size 200×120. From my tests (loading 40 images of size 300×300) I would probably say that the problem is NOT due to imageNamed in his case. Sure it might be, but it is NOT the first thing that I would suspect. So, don’t assume the problem is with imageNamed until you are sure it isn’t due to some other memory usage in your code.

    Ok, that will have to due for now. I will try to find time to test out the other idea I have (I am a little exited that it will work), but I probably won’t get to it right away. When I do I will post back here.

  22. 22 Daniel Alexander Jun 3rd, 2009 at 9:03 am

    Oh I did want to give you some figures that might help you.

    For my 300×300 optimized PNG images the display time is on “average”:

    0.008 to 0.01 second

    Interlacing slows the display down and so does using more colors, but keeping the images looking good (high enough colors) the above is the range of average display times.

    The range of maximum display times is:

    0.012 to 0.024 seconds

    Note that sometimes you might see a time that jumps way up outside of this range, but so long as it is not a frequent occurrence it should not mess up your app. The message will just wait on the message queue, but it will still get processed faster then the user will be able to notice (again, provided such hiccups don’t happen often).

    So, if I was writing an animation app using 300×300 images like the ones I am using. I might want to give each frame as much as 0.028 or 0.03 seconds to display (that is, the seconds per frame should be in that range), but I could probably go down to as little as 0.024 and get away with it.

    So, lets say I was happy with 0.024 seconds per frame (though I wouldn’t be comfortable with it being so close to the actual display time, maybe a 10%-40% buffer would be nice). That would mean that I could display those frames at a maximum of 1 / 0.024 or approximately 42 fps. Well, 42 fps is much higher then I would need (as I said TV is about 23 for NTSC or 29 for PAL). So, this would be great news if I was writing such a program. At say 25 fps I could get away with 1 / 25 or about 0.04 seconds per frame (giving me a buffer of over 40% of my actual image display times–that should be good enough for any hiccups).

    Anyway, this is real data that you might be able to use to gauge where your animation problem may be. If your images are similar size to mine and you are trying to animate over 42 fps then you will definitely have problems. Even if you are animating at 35 fps you might have problems. Heck you could even theoretically have problems at 25 fps, but the likelihood goes down drastically as you move downward from 42 fps (again, if your images are similar to my test images). You could do all these calculations for yourself and then you can know for sure what is going on in your code.

    The point is that once you know what is going on with your fps, seconds per frame, and display times (maximum being important). You have a huge amount of wiggle room to adjust things to get your code to work just fine.

  23. 23 Daniel Alexander Jun 3rd, 2009 at 12:11 pm

    Let me just clarify something. In the above example I am working with maximum display times and I am saying that I would be comfortable working at 10%-40% above those maximums. However, this is just my comfort zone. Even if you went below the maximums to achieve higher fps you would likely be OK. It kind of depends on the animation. It just a matter of how much you can get away with before you don’t like the looks of the animation anymore. The point is that if you go below the maximum display times then your timer messages will start to queue up and things will start to look more and more sloppy.

    Actually, its a little stranger than that since I suspect that the message queue is a priority queue. So, your timer messages might get processed first before your drawRect messages. Remember the timer functions do not take long to complete as they simply increment a current frame counter and fire off a message to drawRect. Its the drawRect that takes the time. So, what you might get is drawRect messages stacked on the message queue. I haven’t really thought it through exactly, but the point is that some type of messages will queue up and the result is that it will effect the flow of your animation (choppy, sloppy, jumpy, maybe slow, lagging, skipping, going up a hill to fetch a bail of water). How much the flow is effected MAY be within the tolerances of the average users (viewers), but at some point it will NOT. So, you could probably go down below the maximum display time to some point, but where that point is is subjective. My point is that if you keep above the maximum display time you don’t really have to care about such things.

    So, because I say that I would personally be comfortable with 10%-40% above my measured maximum display times, I am just saying that that is what I am comfortable with. You can still go below those times to some point and still produce a nice animation. You would know that some messages are being queued, but you would also know that that is OK since you are managing the damages of such message queuing.

    By the way, what I actually do is calculate the display times for ever frame and that way I can get an idea of the distribution of display times. The standard deviations might give me some idea of how far below the maximum display times I can go (as a rule of thumb) before I might start to run into problems (though I would just eyeball it myself…can’t be spending time figuring the standard deviations). Still, the problem I see with this kind of thinking is that it might run fine on my new iPhone, but how might it run on an older model iPhone or on an iTouch. Its just easier for me to try to give myself as much of a buffer as I can and keep my “seconds per frame” above my “maximum display time”.

    I find it is not too hard to get it even to about 40% and still have quality images. So, I don’t think there is a need to push things. Thus I might recommend that you keep your frame times above 10% of your “maximum display times”. But it is up to you what you are comfortable with.

    Ok, I’ll shut up now. I think I beat this subject to death (at least for now).

  24. 24 Rohit Jul 8th, 2009 at 4:12 pm

    Many thanks for this tip!! Helped me a great deal. Yours was the first post I hit upon when I faced this issue and boy what a perfect solution you’ve given… fantastic! many thanks again!!

  25. 25 Malcolm Aug 11th, 2009 at 8:56 am

    I regularly bash my head against the desk over these issues outlined here. I am considering dropping imageNamed. I have a large map image & imageNamed causes no end of grief for me & my users.

    After some quick tests. Yes. using imageWithContentsOfFile does load slower; however using this approach my memory footprint drop by no less than 10Mb immediately! Memory spikes also recovered quickly.

    And, Once you’d scrolled around the CATiledLayer a bit and zoomed in & out there was little difference. There seems to be some magically massive overhead with using imageNamed with large images. And when that caching goes wrong, the only fix is a complete shut down & restart of the device to clear it.

    Now mulling wether or not inflicted the cost/benefit on my users.

  26. 26 Marcel Sep 26th, 2009 at 1:52 pm

    Thanks a lot for this code Alex, it REALLY improved the performance of my (first) app.

    Using it in a UITableView which updates on demand and it really saves a lot of memory, compared to imageNamed: – even the performance is better!

  27. 27 Malcolm Oct 5th, 2009 at 4:23 am

    I have rolled out version 4.0 of Tube Deluxe using imageWithContentsOfFile instead and it has significantly improved the stability of the Map outta sight. Instrumenting the memory usage, it essentially hands it over to springboard to render so the magical disappearance of 10Mb I reported before doesn’t hold true. But it sure is more stable.

    I still use namedImage for many other icons etc. I am so suspect of the system cache, this is what things like CATileLayer are supposed to exploit, but it’s just not stable for large image use (png’s), core graphics CATileLayer doesn’t seem to suffer the same fate.

  28. 28 Bbza Dec 6th, 2009 at 7:17 pm

    Hi Daniel Alexander, Alex
    I have 60 Images that I am animating. Each image is about 3K in size. I have used UIImageNamed which crashes very predictably. Using UIImageWithContentsOfFile works, but the initial animation loop struggles and stutters, after which the playback is great. I dont have any memory issues now. But, I wonder why my initial animation(the first time I show the 60 images animating) loop struggles. I have already loaded the 60images as UIImage objects in arrays before i start the animation. The first loop struggles while all loops after that seem to playback fine. Any ideas why? And how I can solve it?
    Bbza

  29. 29 Alex Dec 6th, 2009 at 7:39 pm

    @Bbza:

    Are you using UIImageView’s -startAnimating and -stopAnimating calls? If not you might try that and see if it does all the appropriate caching before starting. If not I don’t have any other immediate susggestions, sorry.

  30. 30 Daniel Alexander Jan 1st, 2010 at 11:29 pm

    In my test I used sets of 40 images that were of dimension 300×300. The PNG file sizes were about 30 KB each. I generated 10 sets of 40 images and started to run into trouble after loading the 4th set. So, I would think that 60 images of size 3 KB should work with ImageNamed. I suspect that any problems you are having with ImageNamed is due to something other than using ImageNamed. So you should probably review your animation code to make sure it is functioning properly.

    I should be careful here since you didn’t tell me what dimension your images were. Its not so much the file size that matters as it is the dimensions of the image. The image will be loaded and then it will be decoded so that it can be displayed on the screen. The decoded image size will be much larger than the PNG file size. It is this size that really matters and it is dependent on the actual image dimensions (not the dimensions of the object you are putting it into, but the actual dimensions of the PNG).

    The stuttering or slowness of your animation using WithContentsOfFile is due to the fact that you are using WithContentsOfFile. You should expect shuttering or slow animations if you use anything other than ImageNamed (thus the paradox we are all complaining about). As I understand it, using ImageNamed decodes the PNG to be displayed during the loading of the file, but other methods do not decode the image until it is being displayed (they just load the PNG). Thus ImageNamed is faster at actually getting the image onto the screen than the other methods because it simply displays the already decoded image where the other methods still have to decode the image each time it is displayed. Mind you using some of the built in animation methods will do some of this for you as well (so what Alex is saying is also worth a try).

    I should mention that I wrote all the above messages and did my testing on V2.x the Dev. After V3.x came out I did do a bit more testing and Apple has made a number of changes that relate to this issue, but they really didn’t fix the problem. They just made it a bit different. So, there may be some differences that I have not tested, but in general the problem still exists.

    Now there is one thing to also consider. If you change anything about the image before you display it. Like, if you have a 300×300 image that you load using WithContentsOfFile or ImageNamed and then you display that image into a 200×200 ImageView then that will force the image to be re-decoded and you will also see some of these strange effects (shuttering, slowness, etc). Also if you overlay multiple ImageViews it also causes such effects, as it probably requires some manipulation of the images (attempting to display only those portions that are visible). My tests simply displayed a set of 40 300×300 images into a 300×300 ImageView. Thus there was no re-decoding of the image after it loaded (by ImageNamed). When I ran the same code displaying the image into a 200×200 ImageView the animation slowed to a crawl. So, you lose all the advantage of using ImageNamed if you manipulate the image after it is loaded (like resizing it into a different sized ImageView).

    In any case, you should consider using ImageNamed again, and review your code to figure out why it is crashing on you. I am surprised it is crashing, though as I said you only told me the file size of your PNG (I am assuming your image dimensions are also small, which is a lot to assume). What I would need to know is the dimensions of those images (and the dimensions of the object you are putting them into and whether you are manipulating the image in any way, or whether you are overlaying images, etc).

  31. 31 Daniel Alexander Jan 1st, 2010 at 11:55 pm

    I am thinking that Apple had made a change to the way WithContentsOfFile is working (between V2.x and V3.x). I suspect that the image is loaded into memory without it being decoded (as usual), but then during the first time you display the image it is decoded. So, the first time the image is displayed it is also decoded at that time and the decoded image is then queued (thus it is slow the first time it is displayed). However, on subsequent animations the queued decoded image is used, and thus it only needs to display the queued image and no decoding is required. If this is true then that means that either they changed the way WithContentsOfFile works (as it did not do this when I tested it), or you are using some other method that takes care of the queuing of the decoded image (like what Alex was suggesting). I don’t know which, but I suspect that it is happening as I am stating.

    In any case, however it is happening you simply need to get your code to decode the images after or during the initial loading. This means that you could do the animation (hidden) after you load the images or you could simply use ImageNamed (which will decode the images during the load). I have found that even when you try to do things in a hidden way (setting the Hidden property or hiding the animation behind another ImageView) it doesn’t seem to do what you want it to do. I haven’t tried it for this particular problem, but I did do some tests to try to solve my problem by toying around with hidden ImageViews and it didn’t work. I think that displaying an image onto a visible object is decoded differently than when it is being displayed onto a hidden object and as such it still ends up having to be re-decoded when you try to actually view the animation (hope you follow that because I can’t seem to explain it correctly). The point is that you are probably better off getting ImageNamed working. Still, I think that the above is likely the reason you are seeing this shuttering effect on your first animation. There is a decoding taking place during the first time you animate the images that is not happening during subsequent animations. The only way I know to get rid of that is by using ImageNamed.

  32. 32 Daniel Alexander Jan 2nd, 2010 at 12:14 am

    I have to guess at a lot of stuff here so I apologize if I am rambling a bit. I suspect that you are already using the built in animation methods and it is those methods that are queuing your images. However, since you are using WithContentsOfFile the first time they are animated they need to be decoded. The time it takes to decode the images is substantial, so the first animation ends up being slow (shutters). After the first animation the images have been decoded and are queued (by whatever process you are using to display). Subsequent animations can then use the queued decoded images, and thus subsequent animations are fast and smooth (since they do not need to decode like the first animation did). This is a guess since I don’t have time to test it, but I think this is correct.

    Unless you use ImageNamed, I don’t think there is any solution to your problem. If you use any methods other than ImageNamed then the images will need to be decoded before the first time they are displayed. That means that the first time they are displayed will take more time than subsequent times (provided the decoded images are queued by whatever method you are using…otherwise the images will always be displayed slow since they would always need to be decoded during displaying…but that is not the case for your code, so whatever you are currently doing is queuing the decoded images after the first displaying). Your problem seems to boil down to simply getting the images to queue before your first animation. The only way I can think to do that is by using ImageNamed (since it decodes during the load).

    Still, please remember that any manipulation to the image during displaying will cause the image to be re-decoded even if you use ImageNamed. Thus even if you use ImageNamed you could still see some problems provided you are manipulating the images in some way.

    Ok, that was a lot of iffy stuff from me. I hope something in all my gibberish was helpful to you.

  33. 33 Dan Lingman Feb 1st, 2010 at 7:47 pm

    The issue seems to be more that the decoding is required to decode _all_ of the images in the image array before the first frame is rendered, although by then, the UIImageWiew is smart enough to know that it should be on frame 8 or so.

    I’ve tried splitting things up, and I can quite clearly see the startAnimating is blocking until the decoding is done. Heck, my button doesn’t even pop back up.

    I’m doing this right now, as simple and stupidly as possible – load the iamges into an array during the view load. (they are 60 images, all 200×200 png files). I set my animation images, set the duration, set the repeat count, then hit startAnimating. which hangs for 1/2 a second, and my animation starts on frame 8.

    I’ve tried a variety of things to fix this (some reasonable, some rather insane), and I’m at the verge of giving up and writing my own timer based code. Decode time for the the each frame should be around 0.5/60, (based on how long it takes for startAnimating to creak to life) or 0.008 (ie 1/10th of my frame duration), so I think I’ll still be ok.

    Sigh. Giving us a simple and straighforward animation api is awesome. having it choke and burn the first time you try to run it, is not.

    Dan.

  34. 34 Daniel Alexander Feb 5th, 2010 at 11:47 pm

    Yea, I would just dump the built in animation. You can animate using a timer and you will have more control over it. All of my tests where done using a timer and not using any of the built in animation routines. Initially I tried using the built in animation, but most times I want to interact with the animation while it is animating (like stop an animation in the middle and do something different) and that is easier with a timer. Most importantly, using a timer allows me to actually time the drawling event. A lot of animation problems occur when your drawling routine takes longer than your duration (frame rate in the case of the timer). With a timer you can time the event to find out if you are giving it enough time for it to complete. The built in animation is good for simple on screen transitions or some such thing, but for any real animation (particular those involving user interaction) you should stick with a timer.

  35. 35 Daniel Alexander Feb 6th, 2010 at 12:34 am

    Now, I am a little confused over your duration settings. What you need to do is first decide on what fps you would be happy with. That might be something like 23 fps, because such will give you a smooth animation without being too fast. To simplify our calculations in this example lets just say you want to animate at 30 fps. From that your 60 frame animation should have a duration of 2 seconds. At this rate each frame would have 0.033 seconds to render on the screen. Now, for my examples I was displaying 300×300 images at an average rate of 0.008 to 0.01 seconds with maximum display times of 0.012 to 0.024. So, giving my images 0.033 seconds to display should work fine since even my maximum display times were faster than 0.033 seconds.

    Ok, so what I don’t really know is what your durations are. In my above example I simply assume that your animations are 2 seconds in duration, but maybe you are using 0.5 seconds for your duration. If that is the case then that is where your problem is. For example, if you use a 0.5 seconds duration then your no longer animating at 30 fps. Instead you are animating at 120 fps. That would mean that you are giving each frame only 0.00833 seconds to display. The vast majority of frames would not be capable of rendering in that time. And that might cause all sorts of strange animation behaviors.

    Now, the thing is you don’t need to animate that fast anyway. Of course, it is best to know that prior to generating your images because sometimes it is not easy to re-sample to a lower frame rate (you sometimes get a choppy result). So, if your duration is giving you frame rates that are above 40 fps then you should probably get your fps lower. You can do that by selecting ever other frame or every third or every forth frame. Less frames at the same duration will give you a lower fps and thus each frame will be given more time to render.

    This is just a guess since I don’t know enough about your actual durations. Though if I am readying this right:

    0.008 (ie 1/10th of my frame duration)

    that would imply that your frame duration is 0.08. That would mean that you fps is 12.5 fps which is probably too slow. That would make your animation duration about 4.8 seconds. Still, if that is the case then that should definitely have been enough time to display each frame. So long as you are using ImageNamed to load your images then 0.08 seconds is just a huge amount of time to display a frame. Though if you are not using ImageNamed then that would be the problem. You need to use ImageNamed.

  36. 36 Paul Squyres Feb 13th, 2010 at 8:53 am

    I’ve read all the posts so far about the animation issue. I too am having the same issue. My 185 image array is “stuttering” for about 1/2 second when it FIRST plays the animation. After than, it is smooth. Several suggestions have been given to create your own animations using a timer. How do you do this? Can someone provide some sample code to show me how to create an animation for an array of images using a timer?

  37. 37 Daniel Alexander Feb 24th, 2010 at 7:52 pm

    Ok, first thing you will need to do is to load your images. I would load them into an common C array of CGImageRefs. Like this:

    for (int i=1; i<=185; i ) {
    NSString *cname = [NSString stringWithFormat:@"d.png", i];
    UIImage *img = [UIImage imageNamed:cname];
    myThings[i] = CGImageRetain(img.CGImage);
    }

    This assumes that your filenames are 3 digit png's (like 001.png, 002.png, … 185.png) and that all of the images are loaded into your app (there is some trickiness here in getting them loaded so that imageNamed finds them without having to reference them with the entire path, but usually it will default to the proper settings. If your images come up all black then that is because imageNamed is not finding them in the path.

    Ok, so that should load your CGImageRefs into the myThings array. Remember that somewhere in your code you will need to release those references. Something like this:

    for (int i=1; i 185) {
    currentFrame = 185;

    [myThingyTimer invalidate];
    //[myThingyTimer release];
    }
    else {
    [self setNeedsDisplay];
    }
    }

    This essentially walks through each of your frames and triggers drawRect. When it reaches the last frame you will need to invalidate the timer. Now, you have to create the timer somewhere and put it on the run loop. This I can’t remember off hand so I will look that code up. I know the code, but I can’t remember if you have to release the timer and recreate it every time or if you can just put it back on the run loop after it has been invalidated. Oh heck, lets just release and recreate it each time. So, you might add a [myThingyTimer release] to the myThingyTimerFired code above where I put it in comments. Now, that depends on how you are doing things in your code. Basically you will need to invalidate and release the timer before you recreate it. In other words, lets say you want to have an animation occur ever time the user touches the UIView. So, each time the user touches the UIView you will create a new timer. After the animation completes you will need to invalidate and release the timer so that it could be recreated for the next time the user touches the UIView. Where that is done is a question that relates to what you are doing in your code.

    Now, keep in mind that I think that you don’t have to recreate the timer. I think you could just create the timer once and simply put it on the run loop and invalidate it to remove it from the run loop. But I can’t remember so for this example I am just going to release it each time and recreate it.

    So, here is the code to create the timer.

    myThingyTimer = [[NSTimer timerWithTimeInterval:0.01 target:self selector:@selector(myThingyTimerFired:) userInfo:nil repeats:YES] retain];
    [[NSRunLoop currentRunLoop] addTimer:myThingyTimer forMode:NSDefaultRunLoopMode];

    Just remember that you need to match up your timer release with this timer creation. If you repeat this creation code then you will need to have done a release in between.

    Ok, we are almost there. The last thing we need to do is create a drawRect to put the images into our UIView. That might look like this:

    - (void) drawRect: (CGRect) aRect
    {

    CGContextRef viewContext;

    if ((currFrame > 0) && (currFrame <= 185)) {

    viewContext = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(viewContext, 0, self.bounds.size.height);
    CGContextScaleCTM(viewContext, 1.0, -1.0);
    CGContextDrawImage(viewContext, self.bounds, myThings[currFrame]);
    }
    }

    I think you need to flip the context so that the image doesn't get displayed backwards. So, I put that in there with the translate and scale. I can't remember exactly what that should be, but I think it is right. If your images are showing up backwards or upside down then it is those two commands that is doing it. Maybe comment them out or you could mess around with the values I used to get the image to be displayed properly. I think it is right though. Anyway, this will basically draw the image using the CGImageRef that you have in the myThings array at index currFrame. This function is called each time the timer triggers because you have a [self setNeedsDisplay] in your timer fire function (myThingyTimerFired). Also in the myThingyTimerFired function you increment currFrame so each time it triggers it advances to the next frame (image) in your animation. And that should do it.

    You have to forgive me any typos, and my memory is not perfect so I may have made some mistakes above. I didn't get this from any code so it isn't tested or anything, but I think it should get you a good start.

  38. 38 Daniel Alexander Feb 24th, 2010 at 8:20 pm

    One more thing. In this line,

    myThingyTimer = [[NSTimer timerWithTimeInterval:0.01 target:self selector:@selector(myThingyTimerFired:) userInfo:nil repeats:YES] retain];

    I am creating the timer. I set the interval to 0.01. That might be too fast for your images. You didn’t tell me the dimensions of your images so I can’t say whether you can get your animations running that fast.

    In any case, most of the problems are due to what duration you are using. I think you can get a decent animation if you animate as slow as 23 frames per second, but it all depends on what you want the animation to look like. That being said, to get your animation running at 23 fps you would have to use a duration of 1/23 = 0.043 seconds. This means that your animation would take 0.043 * 185 = 7.955 seconds.

    If you kept the duration at 0.01 then that would mean you are animating at 1/0.01 = 100 fps. That is probably too fast. It is probably borderline, but it is definitely too close to your limit. Of course, your images could be tiny and then it might be ok. But this isn’t bad news. If you are trying to animate at this speed then you can cut your animation by 1/4 by selecting ever 4th frame and just using a 25 fps rate. It usually will look similar.

    In any case, you should start your testing at a higher duration and work your way down to see where you start having trouble.

  39. 39 Daniel Alexander Feb 24th, 2010 at 8:37 pm

    You know I think I saw some similar trouble when I tested out the changes between V2 and V3 of the iPhone SDK. What I noticed was that they fixed the memory overflow when using ImageNamed (to some extent), but it caused a bit of lag to show up at some point in the animation. So, whatever frames they are now releasing have to get reloaded and that causes it to lag the animation a bit. This was not a problem in V2, but it is a problem I noticed with V3. I didn’t test it as much as I would have liked. But this might be a new thread for us here (“imageNamed in V3 SDK is more evil”). The solution might be to simply reduce your image size (actual image dimensions).

    Oh, and I think that this problem showed up if your image enters into the status bar area. This was a little strange. The status bar at the top of the screen can be hidden, but there is a little bug that doesn’t actually get rid of it. It is not visible, but it is still sitting there keeping you from capturing touches and such. Additionally, if you hide the status bar and then you draw an image into that region of the screen the image has trouble being animated. Drawing the image into that region effects the timing and sometimes gives you a choppy animation. It is the same thing as drawing other views over top of your animation view. If you overlay any view over any part of your animation view it will cause trouble with your animation timing. The same thing goes for the status bar. So, even if you hide the status bar it will mess up your animation if you place any part of your animation image in that region.

    What else…Oh, I’m just repeating myself. That is everything I can think of. Don’t resize, don’t overlay other things on top of your animation image, don’t draw animation image into the status bar region (or off the screen for that matter), and don’t set your duration too fast. Even then you might have trouble with V3 SDK.

  40. 40 Daniel Alexander Feb 24th, 2010 at 8:44 pm

    Hey, part of my messages are being chopped out. Like lots of my code message is lost. I’m not sure why that is. Let me try to post it again….here goes….

  41. 41 Daniel Alexander Feb 24th, 2010 at 8:49 pm

    It won’t let me repost. Very strange. Let me break it up.

  42. 42 Daniel Alexander Feb 24th, 2010 at 8:49 pm

    Ok, first thing you will need to do is to load your images. I would load them into an common C array of CGImageRefs. Like this:

    for (int i=1; i<=185; i ) {
    NSString *cname = [NSString stringWithFormat:@"d.png", i];
    UIImage *img = [UIImage imageNamed:cname];
    myThings[i] = CGImageRetain(img.CGImage);
    }

    This assumes that your filenames are 3 digit png's (like 001.png, 002.png, … 185.png) and that all of the images are loaded into your app (there is some trickiness here in getting them loaded so that imageNamed finds them without having to reference them with the entire path, but usually it will default to the proper settings. If your images come up all black then that is because imageNamed is not finding them in the path.

    Ok, so that should load your CGImageRefs into the myThings array. Remember that somewhere in your code you will need to release those references. Something like this:

    for (int i=1; i<=185; i ) {
    CGImageRelease(myThings[i]);
    }

    Where you do this depends on whether you are switching out the 185 images for another set of 185 images. If you are just keeping them in memory for the running of your app then you would do this in the dealloc, but you need to do it somewhere.

  43. 43 Daniel Alexander Feb 24th, 2010 at 8:50 pm

    Ok, so far the only iVar you need is the CGImageRef array, but you will also need some indicator of what frame you are animating. Let call it currFrame. And you will need some timer (we will talk about that shortly). So, your class definition will look something like this:

    @interface myThingyAnimationClass : UIView
    {
    CGImageRef myThings[185];
    int currFrame;
    NSTimer *myThingyTimer;
    }
    //put whatever methods you create here
    @end

    Note that everything I am talking about so far will be contained in the implementation of this myThingyAnimationClass. The only thing outside this class for our simple example would be creating an instance of the myThingyAnimationClass and putting it as a subview of some other parent view (or whatever). And I should repeat at the time of the creation of that instance you will give this myThingyAnimationClass a frame that must be the same size as the actual dimensions of your images (it doesn’t have to be, but if it is not then you might experience some slowness to your animation due to the resizing of the images). This is worth repeating as it can be substantial.

  44. 44 Daniel Alexander Feb 24th, 2010 at 8:51 pm

    Anyway, back to the myThingyAnimationClass. You will initialize currFrame to whichever frame you want to start at (probably 1). And wherever you initialize currFrame you probably want to trigger a call to drawRect (we will talk about that below). This is because you probably want your first frame to be displayed even though no animation is taking place yet. If that is not the case then you need to initial currFrame to 0 so that the first frame will show up when the trigger is fired.

    And that leaves us with two last things to deal with. You need do draw a frame in drawRect and you need to trigger the call to drawRect (and the increment of currFrame) in a timer. We already added a timer to our class iVars called myThingyTimer. So, the first thing we need to do is to create a function to handle the timer. We will call it myThingyTimerFired. It might look something like this:

    -(void) myThingyTimerFired:(NSTimer *)timer {
    currFrame = currFrame 1;
    if (currFrame > 185) {
    currentFrame = 185;

    [myThingyTimer invalidate];
    //[myThingyTimer release];
    }
    else {
    [self setNeedsDisplay];
    }
    }

    This essentially walks through each of your frames and triggers drawRect. When it reaches the last frame you will need to invalidate the timer. Now, you have to create the timer somewhere and put it on the run loop. This I can’t remember off hand so I will look that code up. I know the code, but I can’t remember if you have to release the timer and recreate it every time or if you can just put it back on the run loop after it has been invalidated. Oh heck, lets just release and recreate it each time. So, you might add a [myThingyTimer release] to the myThingyTimerFired code above where I put it in comments. Now, that depends on how you are doing things in your code. Basically you will need to invalidate and release the timer before you recreate it. In other words, lets say you want to have an animation occur ever time the user touches the UIView. So, each time the user touches the UIView you will create a new timer. After the animation completes you will need to invalidate and release the timer so that it could be recreated for the next time the user touches the UIView. Where that is done is a question that relates to what you are doing in your code.

  45. 45 Daniel Alexander Feb 24th, 2010 at 8:51 pm

    Now, keep in mind that I think that you don’t have to recreate the timer. I think you could just create the timer once and simply put it on the run loop and invalidate it to remove it from the run loop. But I can’t remember so for this example I am just going to release it each time and recreate it.

    So, here is the code to create the timer.

    myThingyTimer = [[NSTimer timerWithTimeInterval:0.01 target:self selector:@selector(myThingyTimerFired:) userInfo:nil repeats:YES] retain];
    [[NSRunLoop currentRunLoop] addTimer:myThingyTimer forMode:NSDefaultRunLoopMode];

    Just remember that you need to match up your timer release with this timer creation. If you repeat this creation code then you will need to have done a release in between.

    Ok, we are almost there. The last thing we need to do is create a drawRect to put the images into our UIView. That might look like this:

    - (void) drawRect: (CGRect) aRect
    {

    CGContextRef viewContext;

    if ((currFrame > 0) && (currFrame <= 185)) {

    viewContext = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(viewContext, 0, self.bounds.size.height);
    CGContextScaleCTM(viewContext, 1.0, -1.0);
    CGContextDrawImage(viewContext, self.bounds, myThings[currFrame]);
    }
    }

    I think you need to flip the context so that the image doesn't get displayed backwards. So, I put that in there with the translate and scale. I can't remember exactly what that should be, but I think it is right. If your images are showing up backwards or upside down then it is those two commands that is doing it. Maybe comment them out or you could mess around with the values I used to get the image to be displayed properly. I think it is right though. Anyway, this will basically draw the image using the CGImageRef that you have in the myThings array at index currFrame. This function is called each time the timer triggers because you have a [self setNeedsDisplay] in your timer fire function (myThingyTimerFired). Also in the myThingyTimerFired function you increment currFrame so each time it triggers it advances to the next frame (image) in your animation. And that should do it.

    You have to forgive me any typos, and my memory is not perfect so I may have made some mistakes above. I didn't get this from any code so it isn't tested or anything, but I think it should get you a good start.

  46. 46 Daniel Alexander Feb 24th, 2010 at 8:52 pm

    Ok, that seemed to work. So, just ignore that first coding post I did earlier. It doesn’t make sense with all the lines missing. Maybe it was just too long. Hope that helps.

  47. 47 Paul Squyres Feb 25th, 2010 at 8:52 pm

    Hey Daniel,

    I really appreciate your help. I am trying your code example with the timer and will let you know how it goes. What you did makes sense, I just didn’t know how to get started with it. I hope this approach will work as I am getting tired and frustrated with it all. Thanks Again!!!

  48. 48 Daniel Alexander Mar 1st, 2010 at 11:41 am

    I typed more than I had too. It is not as hard as appears from what I typed. Put simply you need to create a subclass of a UIView. And most everything you need to do will be in that subclass. The iVars you will need will be your array of CGImageRefs, an integer called currFrame that tells drawRect which frame to draw during animation, and a timer to walk you through the animation.

    The timer basically increments currFrame and causes the UIView to redraw itself (which means forces a call to drawRect). drawRect is then called by the system and inside drawRect you draw whichever frame currFrame is pointing too (indexing).

    Thus, each time the timer fires you get a new frame drawn and that is the animation.

    All of the code is pretty much listed out for you in my mess of texts above, but in general it should be pretty simple to get it going….

  49. 49 Daniel Alexander Mar 1st, 2010 at 11:50 am

    …get it going yes, but that doesn’t mean it is going to work. All the other stuff I am saying above is to give you some insight into where the pitfalls are. For instance, you need to make sure your images are not too large for your animation duration. If you are trying to animate 300×300 images at over 40 fps then you might run into trouble (particularly if you are also trying to manage user interactions with your animations). This is just one of many problems that you might run into…some of the problems have solutions…some don’t. If your application requires you to do something that the iPhone just can’t do (like, to exaggerate the point, you wanted to animate 300×300 images at 500 fps) then you won’t be able to do it. However, sometimes you can change your requirements a bit (like reduce the number of images you are trying to animate and thus cut your fps down to something more managable). All of the other stuff above is stuff that might help you to work out how you might get around some of these issues.

    What you can know for sure is that you must use imageNamed if you are going to be animating. Short of that you have to figure out ways to get your images and code to balance between imageNamed sillyness and iPhone limitations (etc…). I would also say that you are going to also need to do the animating with a timer like I have above. Those are probably the two things you must do (imageNamed and timer). So, make that your minimum application and try to make it fit in with your data.

    Hope that helps…

  50. 50 Abhishek Mar 29th, 2010 at 6:19 am

    @Alex and everybody,

    It seems that the para at the top of this page might solve my problem and I would really appreciate if you could help…
    Here is what I am doing:
    1.I have a game in which a penguin hits an ice cube with a hammer
    2.if u have hit the correct ice cube then an animation for correct answer is played else an animation for wrong answer is played.
    ..
    ..
    and so on !!

    Well here there are 3 animations in a particular order…
    1. the penguin hits the ice cube…
    2. when the hammer touches the ice cube the ice cube breaks…
    3. depending upon the answer given (right or wrong) the penguin gets happy or sad !!

    now i m using an NSTimer and simply changing the images of the UIImageview
    and all this is really slowing down everything and the game is lagging
    ..
    ..
    CAN u tel me how to use the code u mentioned above in a simpler manner…
    LIKE MAY BE
    how to create arrays with that code which involves BUNDLE and CACHE etc…
    ..
    ..

  51. 51 Daniel Alexander Apr 12th, 2010 at 9:06 pm

    I’m sorry I didn’t notice your message until today…bit late probably. The description of what you are trying to do is just enough to make me unsure whether you need to go with a more complicated approach or whether you can get away with some simpler approach.

    Still, from what you are saying you are changing the .image of a UIImageView inside of your timer handler. The worst thing you can do is this:

    myImageView.image = [UIImage imageNamed: [NSString stringWithFormat:@"d.png", currImageIndex]];

    Where your images are named with a digit followed by “.png” (like 1.png, 2.png, etc). Also the current image that you want to display would be indexed by currImageIndex.

    Now, you are probably not doing this, but that would probably be a big problem (particularly if you are not releasing the image after you use it). Instead, you are probably doing something like this:

    myImageView.image = [UIImage imageNamed: [myImageArray objectAtIndex:currImageIndex];

    Where myImageArray might be an NSMutableArray that you have loaded and stored all your UIImge object in. Since you load your images in you initWithFrame or whereever you only load them once into your myImageArray and the above would work better for you.

    Now, the way I would do it is not to put this stuff in your timer handler at all, rather I would put the painting of the next image in the drawRect and drop the UIImageView altogether.

    In other words, I would create a UIView that loads your myImageArray in the initWithFrame. You would actually have a few different arrays since you have at least three animations (hit ice cube so it cracks, get happy, or get sad), but lets just assume it is one animation for now.

    Ok, so you have a UIView, and you load your animation images on the init (or probably initWithFrame) into myImageArray (which would be your NSMutableArray iVAR). Then in your timer handler you would simply increment through you animation by incrementing the currImageIndex. Something like this:

    currImageIndex ;
    if (currImageIndex > numImagesInAnimation)
    {
    stop the animation timer (invalidate and release it)
    set currImageIndex to whatever you want it to look like at the end
    return;
    }
    [self setNeedsDisplay];

    Then you would create a drawRect function that simply draws the image into the context of the UIView. There are better ways to do this, but to keep it simple you might do it like this:

    [[myImageArray objectAtIndex:currImageIndex] drawInRect:self.bounds];

    Now, I would actually store the Image Context Ref in a C array instead of storing the UIImage in an NSMutableArray and I would then probably draw directly into the UIView graphics context, but the above is simple enough too.

    Most of this stuff I have already gone through above, so I won’t repeat myself (done that enough I think). If you have any specific questions I could try to answer them for you. Hope this helps.

  52. 52 Daniel Alexander Apr 12th, 2010 at 9:15 pm

    Now, you don’t necessarily even need to be using an NSTimer. You could just use the built in animation methods. I mean, it seems like you don’t have a huge number of images and that they might not be very big images. In such an instance it might be better to just do animation without the NSTimer. Just a thought.

    Oh and sometimes there are some applications where you might use the performSelector method (with an afterDelay). I don’t like this approuch at all. In fact, I think using performSelector with the afterDelay is an easy way to get yourself into trouble, but there are some uses for it. Maybe you could use it to help you do your animation if it is not too many frames. I don’t think it would be of any help, but I thought I would mention it.

  53. 53 Daniel Alexander Apr 12th, 2010 at 9:18 pm

    Ahhhh man, this board messes up my posts sometimes. I think the percent symbol messes things up. The above has this line:

    myImageView.image = [UIImage imageNamed: [myImageArray objectAtIndex:currImageIndex];

    and it should read:

    myImageView.image = [myImageArray objectAtIndex:currImageIndex];

  54. 54 Daniel Alexander Apr 12th, 2010 at 9:23 pm

    And I put two plus signs at the end of this line that got removed when I hit submit:

    currImageIndex;

    But it cut them out. So that line should be this instead:

    currImageIndex = currImageIndex 1;

    Its a bummer cause it is selective what it changes…so there are probably all sorts of these mistakes in the code. All I can say is that it was good when I typed it…but that submit button messes it up. Sorry about that.

  55. 55 Daniel Alexander Apr 12th, 2010 at 9:25 pm

    ahhhh…see…it removed the plus sign there too….bummer….it should be

    currImageIndex = currImageIndex insertSymbolOfPlusSignHere 1;

    Why would it cut out a plus sign….very strange…here are a bunch of plus signs…below

    Should have been 9 plus signs above this line…

  56. 56 Daniel Alexander Apr 12th, 2010 at 9:28 pm

    Yep…all the plus signs on this entire page have all been removed and I can’t type one now….
    :(

    You will have to use your imagination a bit reading these posts.

  57. 57 Abhishek Jun 10th, 2010 at 10:27 pm

    @Daniel Alexander (ofCourse)

    Sir,

    First of all I really want to THANK YOU for taking time out and responding to my problems…
    I mean I posted for help but it was so late…i mean all the other posts are like a year old
    ..
    ..
    BUT STILL U RESPONDED TO MY POST AND EVEN WROTE 5 BIG REPLIES TRYING TO HELP ME WITH MY PROBLEM !!
    ..
    ..
    So Sir,
    Thanks a lot !!

  58. 58 Abhishek Jun 10th, 2010 at 10:34 pm

    Sir,
    I also want to tell you that the Penguin Game for which i asked your help is complete and live on the App Store !!
    ..
    ..
    Please go and just check it out Sir…its named KeyBreaker
    (i want u to see it bcoz u played a very important role for me)
    ..
    ..
    And sir,
    I have a question for you as well…
    I mean i need some more help…

    => As you told, i used imageNamed for this game called KeyBreaker
    BUT NOW i want to make this game for the iPad
    ..
    ..
    And the images folder total size is around 4 mb
    and i m not able to use the imageNamed technique
    ..
    ..
    What i did for the iPod version was that i stored all the images in arrays during the AwakeFromNib function of the project…It worked fine

    BUT if i use the same for iPad then the game crashes during startup loading and gives a message like:
    SIGNAL 0
    and sigbus 10 something
    ..
    ..
    SO HAVE U FIGURED OUT HOW TO STORE IMAGES FOR ANIMATION
    AND ALSO CAN U HELP ME ON THIS PROBLEM OF MINE !!

  59. 59 Abhishek Jun 10th, 2010 at 10:47 pm

    Sir,
    I just build the ode on my iPad and it game the following messages as soon as it launched:

    warning: Unable to read symbols for “/Developer/Platforms/iPhoneOS.platform/DeviceSupport/3.2 (7B367)/Symbols/System/Library/AccessibilityBundles/AccessibilitySettingsLoader.bundle/AccessibilitySettingsLoader” (file not found).
    Program received signal: “0”.

    The Debugger has exited due to signal 10 (SIGBUS).The Debugger has exited due to signal 10 (SIGBUS).
    (gdb)
    ..
    ..
    ..
    ..
    so can u help me now…

  60. 60 Abhishek Jun 10th, 2010 at 10:50 pm

    And when i store the images with the following code:

    alphabet_Array = [[NSMutableArray alloc] initWithCapacity:26];
    for (i = 1; i <= 26; i )
    {
    picName = [NSString stringWithFormat:@"Alphabet_%d.png",i];
    [alphabet_Array addObject: [UIImage imageWithContentsOfFile:picName]];
    }
    ..
    ..

    instead of:

    alphabet_Array = [[NSMutableArray alloc] initWithCapacity:26];
    for (i = 1; i <= 26; i )
    {
    picName = [NSString stringWithFormat:@"Alphabet_%d.png",i];
    [alphabet_Array addObject: [UIImage imageNamed:picName]];
    }

    then i get the following messages:

    2010-06-11 11:18:27.826 KeyBreaker[810:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSCFArray insertObject:atIndex:]: attempt to insert nil'
    2010-06-11 11:18:27.837 KeyBreaker[810:207] Stack: (
    852041337,
    861292157,
    852040821,
    852041023,
    850612251,
    850434453,
    850434289,
    13413,
    828201059,
    828199873,
    827153745,
    827153023,
    827464035,
    827462369,
    827460907,
    819342131,
    851590557,
    851588321,
    827151529,
    827144691,
    12195,
    12120
    )
    terminate called after throwing an instance of 'NSException'
    Program received signal: “SIGABRT”.
    (gdb)

    ..
    ..
    but i think this is my mistake…
    Am i using the right way to implement imageWithContentsOfFile

  61. 61 Daniel Alexander Jun 21st, 2010 at 10:50 pm

    Ok, first when using imageWithContentsOfFile you need to include the resource bundle path. So, you would do this instead:

    alphabet_Array = [[NSMutableArray alloc] initWithCapacity:26];
    for (i = 1; i <= 26; i )
    {
    picName = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], [NSString stringWithFormat:@"Alphabet_%d.png",i]];
    [alphabet_Array addObject: [UIImage imageWithContentsOfFile:picName]];
    }

    This is a little sloppy, and it might have some typos in it. But it is something like this.

    I've not done too much with the iPad. I have one and I do need to run some of these tests on it, but I haven't messed with this sort of animation for the iPad yet. But I will be doing so in the near future, so maybe I can be of a little help to you. Let me look at what else you said. I just wanted to point out that you need the bundle resource path to reach your images. Otherwise it won't find them and it will return nil giving you the crash.

  62. 62 Daniel Alexander Jun 21st, 2010 at 11:09 pm

    Hmmmmm….never saw that error (AccessibilitySettingsLoader). I don’t think that that is a problem with the images though. Whenever I see a problem like this (that I’ve never run into) I tend to suspect that it is IB related. I don’t use interface builder at all. I would recommend that people just avoid using it altogether. I think it causes more trouble than its worth, it slows down your development (go figure), and it makes it harder to debug your app. Most importantly it limits the newer developer and narrows their approach. That’s just my opinion though. I am sure that there will be a day in the distant future when Apple gets IB working in a beneficial way, but for now it is not worth using.

    Oh, and you should also turn on NSZombieEnabled if you don’t already have it on. That will give you a bit more debugging information (mainly if it is related to object retaining/releasing). You do this in the following way:

    In XCODE under the “executables” section you can right click on your project executable and choose “Get Info”.

    In the popup click on the Arguments tab.

    In the lower part of the screen under “Variables to be set in the environment” click the to add a new environment variable.

    Type in NSZombieEnabled and set its value to YES. Then check the checkbox to enable it.

    Now when you run you will get a bit more information in your console and your call stack will have more informative information in it.

    This will probably not be of much help for your current problem, but it is a useful tip in general.

    NOTE: YOU MUST REMEMBER TO UNCHECK THIS ENVIRONMENT VARIABLE BEFORE YOU DO YOUR FINAL BUILD FOR SUBMIT TO APPLE. I think they might reject you if you leave it checked (I am not sure, but why take the risk).

  63. 63 Daniel Alexander Jun 21st, 2010 at 11:54 pm

    I went ahead and bought your App to give you some business. I don’t have any kids so I don’t know that I will find much use for it. But it looks neat.

    I know what it is like to wait for the app to start selling, so maybe I can help out your sales a bit. Hope it is successful for you. You should probably put together a YouTube video of its usage and put the link on your webpage. It is important to avoid negative reviews (and you will get them for the stupidest reasons). A YouTube video is an easy way to make sure that the person buying your app knows exactly what they are buying. That way they are less likely to give you a negative since they knew what they were buying to begin with (though some people will still give you a negative for some silly reason).

    Just to let you know that you should not expect much money from any particular app. Of course, there is the wonderful possibility that an App will go viral and that would definitely be a big payout (I think there are near 100 million devices out there), but you should think instead about the money you can make by developing 20 apps (or some such number). Each may bring in a small amount, but together they can add up to a nice sum. Plus, it can be depressing to get a mere 5-20 sales a day if you are hoping to be selling 100-200 sales a day. However, if you have 20 apps selling 10 (or whatever) a day isn’t too bad. At $1.40 per app (for a $1.99 app) that works out to $102K a year. And that would be residual income. Its good to plan for 20 apps. That way when you get 5-20 sales a day on some app you can get excited about it. Because that’s 5-20 closer to where you want to be.

    That’s about all the advice I have on iPhone development. It boils down to…”Don’t get discouraged by low sales numbers”. If they are 0 then feel bad (You will have at least 1 tomorrow…hee hee). But if they are averaging 5-20 then that isn’t horrible provided you have a plan that involves 20 apps (or whatever). You probably already know this.

  64. 64 Daniel Alexander Jun 22nd, 2010 at 9:14 am

    Ok, I messed around with your App and found a bug or two.

    1. When you select the third letter/number the penguin animation plays. During that animation a thin green line is visible to the left of the penguin (goes down the full screen nearly). Probably just the frame being a little shifted or maybe the images within the animation are smaller (one pixel). The strange thing is that this doesn’t happen all the time. Since it doesn’t happen all the time that probably means that the images are being transformed (scaled probably). Or, I have noticed that sometimes when an image is placed at partial pixel locations (like 200.45f or something such), the iPhone will sometimes misinterpret that to be 201 or 200 at different times. I have also seen the images get blurry due to placing them out of the integer range (in the float innerspace). I have some ideas about what is happening, but maybe that is part of this problem.

    2. Found a Crash. The app will allow you to click the exit/menu button in the upper right even during animations and the voice will continue to play. So, you can click back and then quickly click “Start” and it will then kill the voice and start saying something different. That all seems to work fine, but I thought it strange so I tried to break the app at that point. Here is how to get it to crash.

    a. Click Start.
    b. Complete the first set of questions (3 questions) to get to the sticker screen.
    c. Now you need to do the following quickly…pick a sticker, quickly click on the “Back” button when it becomes visible, quickly click on the “Start” button when it becomes visible (the voice should still be talking at this point).
    d. So, it should have stopped the previous voice and asked a new question (I think it is the same question, which is probably an important observation since normally it would switch questions).
    e. Click on the right answer and the app will crash (I never tried clicking the wrong answer, but it probably will crash too).

    Other than that it seems to run fine. Though it might be nice for it to remember the score. You could have a “Continue” button and a “Start New” button. Continue will start back off with all the stickers from the previous runs until they are finished. It might be nice to put which stickers you have completed in the app Defaults, but you could write them to file instead.

    Putting them in the Defaults is pretty easy though. Normally you might think twice about saving a lot of stuff into the Defaults, but 30 BOOLs (or even integers) isn’t a huge deal.

    I can’t think of anything else. Looks good.

  65. 65 Daniel Alexander Jun 22nd, 2010 at 9:35 am

    Just a quick note about the application defaults. You would do something like this:

    In your application delegate you probably already have this method. Just add in the code to save the sticker state. This assumes there are 30 stickers and that you have an integer array that stores whether they are selected or not (probably it would be a BOOL array). In any case this should give you an idea of what you might want to do:

    - (void)applicationWillTerminate:(UIApplication *)application
    {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    if (defaults)
    {
    for (int i=0; i<30; i )
    {
    [defaults setInteger:stickers[i] forKey:[NSString stringWithFormat:@"S_%d",i]];
    }

    [defaults synchronize];
    }
    }

    And then when your app loads (however you are doing this), in your entry point for your app you will just load these flags.

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    for (int i=0; i<30; i )
    {
    stickers[i] = [defaults integerForKey:[NSString stringWithFormat:@"S_%d",i]];
    if (stickers[i] = STICKER_Undefined)
    {
    stickers[i] = STICKER_Incomplete;
    }
    if (stickers[i] = STICKER_Complete)
    {
    continueOK = YES;
    }
    }

    And somewhere you might declare:

    #define STICKER_Undefined 0
    #define STICKER_Incomplete 1
    #define STICKER_Complete 2

    You don't really need the "if (stickers…" thingy. But I tossed it in because integerForKey doesn't have any default return value for when a value does not exist. So, this is one way you can treat a return of 0 as indicating that the value does not exist so that you can then initialize it on the first run. Probably a better way to do this in this instance is to just make 0 be your default value (#define STICKER_Incomplete 0).

    Like I said, you probably will use BOOLs so you will have to change the code a bit for that, but it will look essentially similar. Hope that is useful to you.

    Note: an alternative is to write the array out to a file. Also remember that once this data is loaded you need some way for the user to reset it (thus you should probably have a "Start New" button and a "Continue" button. I have added a flag above that will be set during the loading that will indicate if any stickers are completed (continueOK). If even one sticker is completed then you can show the "Continue" button…otherwise "Continue" would be hidden or grayed out.

    Note, Note: Whenever I post code this forum seems to delete my signs. So, you might have to check that and put them back.

    Hope this helps. Nice app.

  66. 66 Daniel Alexander Jul 10th, 2010 at 10:52 am

    I don’t yet have an iPhone 4, but I have done a little testing on them with OS4. I have found that there are some differences in the way that image processing works on the iPhone 4. I have run the tests on older iPhones with OS4, so it isn’t exactly an OS4 issue, but rather it is an OS4 with iPhone 4 issue. So, if you have written apps that do image animation similar to the way we are describing here (whether that be in a album view or in a game) you might find that the animations crawl when you put them on the iPhone 4 with OS4. I have not had enough time on the iPhone 4 to actually figure out what exactly is the trouble. When I get one I will test it out further and let you know. Its just a warning that you should test the iPhone 4’s before they sell to many of them and you start getting negative feedback due to such issues.

    Again, the troubles I have experienced where related to the iPhone 4 with OS4. Older iPhones with OS4 don’t seem to have the same issues (or iPads for that matter…they seem to work fine as well…its just the iPhone 4 with OS4 and it is only in some circumstances which I have not determined yet as I did not have enough time with the iPhone 4 that I borrowed to test it completely). You might not see these problems with what you are doing, but you might see them too…so I thought I would mention it. Test your app on an iPhone 4.

  67. 67 Sean O'Connor Jul 12th, 2010 at 12:29 pm

    Thanks Alex, I think you’ve saved my game (“Tribes”)! It was working fine in older iOSes but ever since about 3.2 on the iPad and 4.0 on the iPhone it started crashing immediately on trying to load all the graphics in the game. I was loading about 1,000 small PNGs with imageNamed at the start and that seems to crash the device (but not the simulator).

    It still does crash my iPod Touch when I’ve loaded about 400 PNGs using your imageWithContentsOfFile method but the iPad seems to be immune. What I’ve done is added:

    if ([imageCache count] == 350)
    {
    [imageCache removeAllObjects];
    }

    just before it checks if the filename is in the dictionary. I cut down on a lot of the images too so it only rarely hits the 350 limit now.

    It’s a shame I’ve had to do that though as I’ve had to lose some animation frames and I don’t see why older OSes had no problems.

  68. 68 Daniel Alexander Jul 16th, 2010 at 12:00 pm

    Its important to make note that “small png” can mean two different things. It could mean that the file sizes are small 4kb, 10kb, 90kb? But those file sizes have little effect on the problem you are running into (though there might be some indirect relationship, such can’t be relied on). What is important is the decoded image size. You can encode an image into a small file size by doing all sorts of tricks like using a smaller color pallet (web colors, etc). So, looking at the file sizes of the png’s is not terrible helpful. It is probably more helpful to consider the actual dimensions of your images. Like if you have a 38kb png file that is a 300×300 image then the decoded image size will be a lot more (I’ll guess around 300KB uncompressed). It is the decoded images that need to fit into memory and it is the decoded images that will dictate how fast you can animate.

    Mind you there is still good reason to get your png file sizes down. There is a limit to the app size that can be downloaded through ATT’s network. I have found that people want to buy apps when they are sitting outside with friends or some such. That means that if a user has a choice between waiting until they get home to buy your 11mb app and someone else’s 9mb app that they can download emmediatly they will by the 9mb app (I think the limit is still 10mb, but they may have changed it to 20mb recently…but I don’t think so…I think it is still 10mb). So, smaller png files sizes can be helpful in that regard (there are lots of tricks you can do to bring that down considerably). But those sizes don’t matter when you are trying to animate. Actually, they can hurt your animation speeds because there is more work decoding them.

  69. 69 Daniel Alexander Jul 19th, 2010 at 5:13 pm

    Ok, so here is the issue as I can see. The issue I mentioned earlier (with the iPhone 4) is actually an issue with OS4 (though it is a bit more problematic with iPhone 4). Apparently the OS4 does not draw into a Rect, UIView, or into a device context as efficiently as the older OS’s did (OS3.2-). However, Apple did put effort into increasing the efficiency of Layers. On the older OS’s using Layers actually slowed things down for these types of image animations so drawing onto the device context was fastest. However, with OS4 it is the other way around. Using a sublayer contents to display images for this type of animation is now faster than drawing onto the context (which is now much slower).

    Sooooooo, if your animations all of a sudden slow down to a crawl once you upgrade to OS4 then this is why that is happening. You need to switch your animations over to using a sublayer and updating its contents.

  70. 70 Daniel Alexander Jul 20th, 2010 at 10:01 am

    By the way, you might still want your app to support older iPhones (like the iPhone1, etc). Those iPhones can’t even run OS4. You can use UIDevice to get the systemVersion. If the systemVersion floatVal is greater than or equal to 4.0 then you could use Layers. Otherwise, you would just keep doing what you used to do (drawing into a Rect most likely…or onto a device context of a UIView…or whatever was working with the older iOS). So, just create a simply iVar bool called useLayers that you set up in your init (using UIDevice). Also, add a subLayer (or multiple subLayers depending on how many animations you want to be doing) to your UIViews layer so you can update its contents for iOS4 iPhones. Then in your drawRect drop an “if” statement to determine which way you want to draw your images (into Layers or the old way).

    Remember this is only if you run into trouble with your animations. If the images are small enough you might not see any substantial slowing and then you can just keep doing what you are doing. But if you upgrade to OS4 and your animations start to crawl then this is how you can fix it.

  71. 71 Daniel Alexander Jul 20th, 2010 at 10:16 am

    Just one more note. Although this is an issue with iOS4 on any iPhone, the problem is more dramatic using the new iPhone4. Also, note that when I draw into the device context of an older iOS iPhone it is still faster than if I use layers on the same iPhone using iOS4. So, it would be nice to still get that speed if I am using iOS with an older iPhone. And the key is that sometimes you can. It depends on the sizes and number of images that you are using.

    So, for one of my Apps instead of using UIDevice to determine whether to use Layers or not use Layers depending on the iOS version number. I decided I would get the actual device model using sysctlbyname. Although UIDevice will also give you the model it does not distiguish between actual models of the iPhone. Instead it just tells you it is an iPhone. sysctlbyname will give you the additional information you need to distinguish between the different types of iPhones.

    Here is my set of Defines for what sysctlbyname returns (It returns a string…I don’t know if this forum is going to post this properly, but I will try anyway…all you really care about is the string literals):

    [code]
    #define DIDRST_Unknown @""
    #define DIDRST_iPhoneSimulator @"i386"
    #define DIDRST_iPhone1G @"iPhone1,1"
    #define DIDRST_iPhone3G @"iPhone1,2"
    #define DIDRST_iPhone3GS @"iPhone2,1"
    #define DIDRST_iPhone4 @"iPhone3,1"
    #define DIDRST_iPad1 @"iPad,1"
    #define DIDRST_iPodTouch1G @"iPod1,1"
    #define DIDRST_iPodTouch2G @"iPod2,1"
    [/code]

    So, now you can determine which devices you want to support with Layers and which you want to support with the older approach. For instance, I wanted all the older devices to NOT use layers even though some of them may have been using iOS4. This was only an issue for one of my apps. On other apps I wanted any device using iOS4 to use Layers (in which case I used UIDevice instead of sysctlbyname).

    Sadly I know someone out there in Internet World there is someone that will need this post (I sure was, but couldn’t find anything). Hope they find it here cause this thread is the extent of my helpfulness. :)

  72. 72 Alex Aug 26th, 2010 at 10:32 am

    1,000,000 THANKS !!!! Just that! 1,000,000 THANKS !!!! You saved me from going crazy with the stupid animation!

    The only problem I have is that the animation goes really fast now. I put a wait() but it doesn’t seem to work. I use a CATransition to switch between two views. Do you know if there is a way to make the animation run slower? I tried the CAtransition.duration but it doesn’t work the right way.

    Again a 1,000,000 THANKS !!!!

  1. 1 Image memory at Under The Bridge Pingback on Jan 13th, 2009 at 10:16 pm
  2. 2 View Controller memory at Under The Bridge Pingback on Feb 7th, 2009 at 7:58 pm
  3. 3 10 iPhone Memory Management Tips | gotnull.com Pingback on Apr 8th, 2009 at 9:21 pm
  4. 4 tewha.net » Blog Archive » imageNamed is evil Pingback on Apr 16th, 2009 at 12:26 pm
  5. 5 akosma software | 10 iPhone Memory Management Tips Pingback on Nov 20th, 2009 at 5:52 am
  6. 6 10 iPhone Memory Management Tips | akosma software Pingback on Nov 28th, 2009 at 3:25 am
  7. 7 atomton » iPhone Image Cache w/ source code Pingback on May 6th, 2010 at 12:05 am

Leave a Reply