Why does addSubview load the view asynchronously - iphone

I have a UIView that I want to load when the user clicks a button. There happens to be some data processing that happens as well after I call addSubview that involves parsing an XML file retrieved from the web. The problem is the view doesn't show up until after the data processing even if addSuview is called first. I think I'm missing something here, can anyone help?
Code: I have a "Loading..." view I'm adding as a custom modal (meaning I'm not using the modalViewController). This action is linked to a button in the navigationController.
- (IBAction)parseXml:(id)sender {
LoadingModalViewController *loadingModal = [[LoadingModalViewController alloc] initWithNibName:#"LoadingModalViewController" bundle:nil];
[navigationController.view addSubview:loadingModal.view];
[xmlParser parse];
}

Howdy! If you're looking for an easy work around:
[self showLoadingScreen]
[self performSelector:#selector(methodToDoWork) withObject:nil afterDelay:0.3];
However you're better off making methodToDoWork asynchronous if you can.

If you are doing your processing on the main thread, it will block the main thread until its done, which means your UI will become unresponsive and not update until the main thread resumes.
You need to perform your XML processing on a background thread using something like NSOperation or an existing asynchronous API and update your view when you have finished.
Its hard to be of more help and get a better idea of whats going wrong without seeing your code unfortunately.

Related

is this code using UIActivityIndicatorView flawed?

Is this code using UIActivityIndicatorView flawed? It appears that I don't actually get to see the indicator/spinner at all here, so is this because the view isn't drawn until the who viewDidLoad completes?
Is the only way around this to do the viewDidLoad custom work (e.g. data updates) on a separate thread? (I was hoping in this case for an easier single-thread operation). Is there a way to force the view to refresh after the "startAnimating" line perhaps prior to the data loading commencment?
Code from UITableViewController implementation:
- (void)viewDidLoad {
// Wait indicator - Start
self.waitView = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
self.waitView.hidesWhenStopped = true;
[self.view addSubview: self.waitView];
// Load data into tableview
[NSThread sleepForTimeInterval: 5.0]; // Test code to simulate
[self.waitView stopAnimating];
}
You should also call startAnimating. Sleeping is not a good idea. I would prefer the performSelector-methods which starts a not recurring NSTimer under the hood.
Try this:
-(void) doStuff:(id)aSender
{
[self.waitView stopAnimating];
}
-(void)viewDidLoad
{
...
[self performSelector:#selector(doStuff:) withObject:self afterDelay:5.0];
}
in addtion: also set the frame- or bounds-property of the ActivityIndicatorView somewhere like sosborn said in his comment
Actually the answer from Thomas should work as it is, I will add a little explanation as to why not use sleep as you have done it.
All the UI processing on iPhone (and most of OSs as well) is being done in only one thread - the main thread, the thread that executes the so called run loop. If you stop that thread the UI will stop, nothing will be drawn.
Putting sleep into viewDidLoad, which runs in the main thread, will do just that - stop UI from doing anything. So because immediately after wakeup you've called [self.waitView stopAnimating] and the activityview should hide when not animating, you can't see it at all - you just didn't give it any time to show.
Thomas used a NSTimer to call stopAnimating after 5 seconds - now this lets the main thread to execute code before stopping animation and hiding waitView and this will work for your test.
Better yet you just let it animate without any timer and use a delegate patter to be informed by the tableView loading code after the data has been loaded, then stop animating. You don't know how long loading of data will last, so it's better to wait until it's finished than stop animating after any specific time.
Oh well, and the size and position, makes sense, but for testing it doesn't matter and is not the cause of not seeing it - if not specified it will be added at 0,0 and have a default size so you will see it anyway.

UIAlertView starts to show, screen dims, but it doesn't pop up until it's too late!

I have a button which when pressed makes calls that load information from a series of URLs (about 5 seconds loading time). Right before actually making those calls I want to add a "loading" alert. When I use a UIAlertView the screen dims like it's about to pop up, but it doesn't until the data is loaded - too late! I have no idea what's going on, it's like the calls I'm making to load the data are immediately taking preference over showing the new view, even though they're made right after the calls adding the new view (or showing the alert).
This is a summarized version of the code:
-(void) refresh{
UIAlertView *av = ...
[av show]; //this should pop up before dat begins to load
[myDataSource loadData]; //this contains a series of [NSData initWithURL] calls
[self.tableView reloadData];
//here I would hide the AlertView, but if I do I see it for just s split second
//when the tableView has already reloaded
}
Thanks in advance for any insight!
***EDIT
To anyone who uses performSelectorInBackground beware of the added complexities of creating what is effectively a threaded program. For example, leaks might appear as the new thread doesn't have an autorelease pool - you have to add one, etc.
when you try to retrieve data from the internet (I guess you are using something like [NSString stringWithContentsOfURL: ...]) the main thread is waiting for those data and for this reason the application cannot redraw the interface.
You can try to use:
[mySourceData performSelectorInBackground:#selector(loadData) withObject:nil];
Be careful if you are using coredata to lock the NSManagedObjectContext before doing any operation on it and unlock it when you finish.
If you have a bunch of operations to perform, in addition to performSelectorInBackground: like cescofry wrote, you can use NSOperation and NSOperationQueue.
You probably don't want an UIAlertView for this anyway. Take a look at MBProgressHUD on GitHub.

show uiactivityview while an other operation is loading

i googled for hours now but i can't find a code example, which describes how to show an uiactivityview while an operation/method is loading.
in my case i want to show it while i load a large image to an uiimageview. have you got an idea how to realize that?
thanks in advance.
sean
You might want to look in the UI UIActivityIndicatorView Class Reference documentation.
Make sure to return to the run loop while waiting for the image to load in order to see the activity indicator animating. Also, you will probably need to create a method to stop the activity indicator, and perhaps call that stop method by doing a performSelectorOnMainThread from whatever method handles the asynchronous completion of the loading.
well its quit easy.
show activity indicator
start thread loading that image
if image loaded, thread tells mainapp to kill activity indicator.
NSThread detachNewThreadSelector:#selector(myMethod)
toTarget:self
withObject:nil];
and
[self performSelectorOnMainThread:#selector(myMethod)
withObject:nil
waitUntilDone:false];
will be helpfully.
taken from http://www.iphoneexamples.com/

Show loading screen while parsing, then refresh UITableView?

I have a UITableView with the following code:
- (void)viewDidLoad {
[super viewDidLoad];
parser = [[XMLParser alloc] init];
[parser parseXML];
My problem is that the launch takes too long because it's parsing everything before displaying the view controller with the UITableView. Also, if I set up another UITableView and parse another XML (in a different tab) I tap to go the other tab, but then it hangs while it parses the other XML, and once it's done, then it display the UITableView.
I have looked for information on when to start the parsing, reload the UITableView and how to show a loading screen while the parsing code runs, but have not been able to come up with anything.
Anyone have any ideas?
You can call something like
[parser performSelectorInBackground:#selector(parseXML) withObject:nil];
on your main thread to run the parseXML code in a different thread. Just be careful to not update the ui from that thread. To update the UI from the parser thread, you'll need to call something like
[self performSelectorOnMainThread:#selector(XMLUpdated:) withObject:self waitUntilDone:NO];
If by loading screen you mean an activity indicator then trying to add the indicator animated before parsing could potentially not work because when you parse on the main thread it blocks and does not let the indicator appear on screen. To get around this i would do the parsing on a background thread, this should allow your indicator to appear, when the parsing is done, ahve the parsing object send a message to your viewController so i t knows its ready to show the tableview. (i should mention that UIKit is not thread safe and you should not try to update any UI elements from the background thread without using performSelectorInMainThread)

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...