Adding subviews to UiScrollView .. smoothly - iphone

I am trying to dynamically add subviews to a UiScorllView in real time (to save memory).
Doing so causes the scrollview to hang for a brief second... what is the way around this?
any hints appreciated

Read up on the how the UITableView solves this problem. Your biggest performance hits here are the allocation and initial drawing of the subview, but primarily the allocation. UITableViews use a reuse identifier and an in-memory heap to keep all its cells in memory during scrolling so it can just reuse cells that have already been allocated instead of re-allocating new ones every time a new cell scrolls into the viewable area.
Perhaps you could implement your own subview reuse system to save yourself all that allocation time. Again, read up on specifically how the UITableView does it and model yours off of that. Unfortunately, there really is no easier solution than that that I can think of.

I suggest that any action that could possibly hang the user interface should be placed in a thread. This way, the process of adding the subview will be done in the background, not disturbing the main thread.
You could either do this by NSThread or you could implement the NSOperationQueue.
Declaration of the NSThread is simple;
[NSThread detachNewThreadSelector:#selector(myFunction:) toTarget:myObject withObject:argumentObject];
Where 'myFunction' should be replaced with the name of a function, the 'myObject' with an object (or simply state self if you want the current class to handle the thread) and 'argumentObject' should be replaced with any object you want to pass along as an argument; this may be nil
The function itself should look like this;
-(void)myFunction {
NSAutoReleasePool *pool = [[NSAutoReleasePool alloc] init];
// do your thing here...
[pool release];
}
Every thread should have an autorelease pool.
One hint; if your subview has to animate, try declaring a UIAnimation block

Related

how to "prepare" a view in the background?

I have a two views, 1st is a simple view with some introduction about usage and by click of a button it opens the main view. The main view has many images and two customized tables with rows consists of text and image, thus the creation of the main view is quite slow. The profiler shows most of the time is consumed by imageIO (decode_mcu, png_read_filter_row, pmap_copy_page and decompress_onepass)
I want to optimize by creating the main view immediately after the 1st view is loaded, and when I click the button, it simply set that view to visible or bring that view to front.
I tried to alloc & init the main view in the first view's viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
rootVC = [[RootViewController alloc] initWithNibName:#"ViewController" bundle:nil];
rootVC.delegate = self;
}
and do this in the button's action method
- (IBAction)buttonUp:(id)sender {
[self.view addSubview: rootVC.view];
}
But this is not working, the loading still takes very long. How could I sacrifice some memory to make better user experience?
Thanks
Leo
You should:
Use Instruments to see where your code is spending the most time, and optimize there. It may not be where you think.
If you are using a UITableView, learn how to create UITableViewCells on demand (rather than preloading a large number) and recycle instances (rather than recreating them).
Only if Instruments points to this as the bottleneck: try loading/decompressing images in on a background queue, then updating the UIImageView (or whatever) on the main queue after loading.
In iOS 4.x or later, you can do this:
NSString *path = ...
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^{
// This is happening in the background thread.
UIImage *image = [UIImage imageWithContentsOfFile:path];
dispatch_async(dispatch_get_main_queue(), ^{
// This is happening on the main thread; all UI updates must happen here.
imageView.image = image;
});
});
P.S. By the way, UIViewController doesn't load the view on creation, but on demand. So based on your code above, the expression rootVC.view is probably triggering the view to load. If you want to force it to load earlier, just put rootVC.view somewhere else.
UIViewController tries to load its view lazily, i.e. the view isn't loaded until it's actually needed. If you really want to force your view controller to load its view, you just need to access its view property, e.g. rootVC.view.
However, you really shouldn't need to do that. Table views, for example, only create the cells that are visible on screen, so it's unlikely that the number of rows is significantly impacting the time to load the view. One caveat here is that there are some things that can really slow down a table view. For example, if you're using variable-height cells in a table with a lot of rows, that can really slow down table creation. Try not to do that.
#benzado gives some good pointers for improving performance. Trying to work against Cocoa-Touch is always a recipe for pain, so try instead to figure out why your view is taking so long to load. Where is your app spending all that time? What can you do to reduce that load?

Adding subview, gets delayed?

i didn't really know how to title this question, but here's a thing that really kills me:
In my app i have a UITableView, UISegmentedControl and UINavigationBar. Once UISegmentedControl gets one of its segments selected i want to show a UIActivityIndicatorView on the UINavigationBar and then parse an xml file and present the results in a table.
Everything works almost as i want it to, except one thing, the activity indicator view gets added to the uinavigationbar after the parser finishes, even though the method showLoading that adds UIIndicatorView to UINavigationBar gets before parser is initialised.
Can anyone explain it? is there something i might be missing? maybe the ui needs to get redrawn?
thanks
peter
It looks that you parse your xml in main thread and so it becomes blocked for UI changes. Try to move xml parsing to separate thread (e.g. by calling your parsing method via -performSelectorInBackground:)
Edit: Actually you're (almost certainly) using autorelease implicitly in your application - as many standard functions return autoreleased objects. When you're running your functions on separate thread you need to create NSAutoreleasePool object there to handle autoreleased objects and avoid memory leaks (see Autorelease Pools in docs). So your parseXML function must look like:
- (void)parseXML{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
... //xml parsing routines etc
[pool release];
}

iPhone - insertSubview makes ObjectAlloc jump

Whenever I add a new viewController my ObjectAlloc jumps up really high and never comes back down. Even after calling removeFromSuperview. Is this normal?
if((UIButton *) sender == gameArcadeBtn) {
GameArcade *gameArcadeController = [[GameArcade alloc]
initWithNibName:#"GameArcade" bundle:nil];
self.gameArcade = gameArcadeController;
[gameArcadeController release];
[self.view insertSubview:gameArcadeController.view atIndex:1];
}
Instantiating a view always creates many objects.As long as this view is in memory or has not been autoreleased, the objects will remained alloced in memory. Thus, to answer your question, this is normal.
It sounds like you are worried about memory usage and while it is important to watch the object allocs so that it doesn't get too it is more important to find your app leaks.
Some memory management tips:
1) do lazy loading. Only load your views when the user asks for them, not all at the beginning of the app
2) remove everything that you possibly can when you dont need it anymore. This means doing tons of work in viewWillAppear and viewDidDisappear
3) learn about #properties and how it relates to autoreleasing, and do not use properties for everything.
4) As appealing as it is, avoid autorelease and manually release objects when you dont need them anymore.
that's probably due to the fact that you're still retaining the view's controller in the class. try releasing that

How do I create a reusable Loading Screen?

How do I create a loading screen that can be reused at any given time. I'm aware of the Default.png but I need the flexibility to plug in a loading screen at any point during the application life cycle.
This is what I have thus far.
//inside a method that gets called by a UIButton
LoadingViewController* loadController = [[LoadingViewController alloc] initWithNibName:#"Loading" bundle:nil vertical:NO];
[self.view addSubview: loadController.view];
//some method call that takes a few seconds to execute
[self doSomething];
//This loads some other view, my final view
[self.view addSubview: someOtherView]
but it seems that the loading view is never displayed. Instead the previous view stays there until the "someOtherView" gets added. I put trace logs and the code does seem to get executed, I even replaced [self doSomething] with a sleep(2), but the intermediate loading view is never displayed.
If I remove [self.view addSubview:someOtherView]; then after a few seconds...(after doSomething finishes executing) the load view is displayed since there is no view that is pushed on top of it, however this is obviously not the functionality I want.
Can explain this behavior? Is there something about the rendering cycle that I am misunderstanding because it doesn't seem like the view (on the screen at least) is instantly updated, even though I call a [self.view addSubview: loadController.view];
Would I need to create a separate thread?
In general, for changes in the UI to be made visible to the user, control must return to the main runLoop. You are only returning to the runLoop after taking the loading view down and replacing it with the other view. One strategy for dealing with this is to move the code that does the loading onto another thread. NSOperation and NSOperationQueue can be used for this.
An even simpler approach is to use performSelectorInBackground:withObject to do the processing. Once processing is complete the UI can be updated again to show the data. It is important to remember that the UI updates must be carried out on the main thread. Use performSelectorOnMainThread:withObject:waitUntilDone: to accomplish this from the loading thread.
This sounds like a lot of complication but it is really as simple as breaking your single method up into three separate methods as follows:
Display the loading view and start the background process - this is the button action method.
Do the background loading - called from the button action function with performSelectorInBackground:withObject.
Remove the loading view and update the display with the data - called from the background thread with performSelectorOnMainThread:withObject:waitUntilDone.
I created a subclass of UIView where I initialized how my loading-view should work and look like. (My view appeared and slided in from the bottom with an nice animation).
I then added code that handled whether the loading-view should be visible or not in a subclass of UIViewController.
I then let all my viewcontrollers be an subclass of my new viewcontrollerclass which made it possible for me to do:
[self showloadingMessage:#"loading..."];
in all my viewcontrollers...

NSOperation + Objective-C Categories = Bad Idea?

I've set up an Objective-C category for an iPhone app's UIImageView class. The category's mission is to help load URL-based images asynchronously with memory/disk caching.
Now, in UIImageView+Cache.m I have access to an NSOperationQueue so I can kick off a loading thread. I create an NSOperation-derived object, initialized with the image URL and the target UIImageView, and a selector to perform on the target once the operation is complete. In the selector method, we set our freshly-loaded image (or, if not found, we set an alternate placeholder image), and we're done!
This works fine, until a UIImageView happens to be removed before the NSOperation completes. For instance, I have a previous/next segmented control in my UI that causes these UIImageViews to be removed and added anew (they're part of a larger "item" that is being viewed in the app), so it's very easy to tap these in rapid succession.
So if you decide to start tapping away before all the images are loaded - KABLAM! Unhappy thread has an invalid object and doesn't know it. :(
The closest thing I can find to help mitigate this is NSOperation's cancel and isCancelled methods, except you can't keep track of which operation object to cancel within a Category, because - if I understand correctly - Categories can't add IVARs to objects!
Maybe that means Categories aren't a good idea here? (Whines: "But I liiiiike Categories! Waaah!")
Advisement appreciated!
I probably wouldn't use a category for this situation. Categories are useful, but are usually unnecessary. I'd only use a category if you have a really good reason to. What exactly are you putting in the category?
I think you could implement the whole thing in the NSOperation subclass, which would be the best solution. Put a retain on the image view so it doesn't get deallocated before the image is downloaded, and cancel the download if the view is not visible anymore. If that's not possible, then subclass UIImageView instead of using a category.
I would say there is no harm in moving this into your own UIImageView subclass. Sure, you may like categories - but if they don't do the job then why hesitate in moving to a design that does?
Are you retaining the UIImageView by the NSOperation? Otherwise the imageView might be freed before the NSOperation completes, leading to kablooi central. You should do a retain and then, once you've done the setImage, do a release.