How can I populate a UIViewController after it's loaded? - iphone

I'd like my UIViewController to appear on the screen with a "Loading..." UILabel in the center and then, once that's been displayed, start loading data to display.
How can I achieve this? I tried loading it in viewDidAppear:, but at this point the view has still not been displayed on the screen, so the app appears unresponsive to users. If I try setting a short timer (0.0001 seconds) so that the view will be displayed and then the data loading method gets called in the run loop after that, for some reason the time it takes from opening the view controller to seeing content is about three times as long as it was without the timer. In this case, the Loading... text appears, but the user has to wait way too long.
What's the best way to do this?

if I have not misunderstand your purpose, I think that you just want sow a 'loading' status to user, right ? if so , I suggest that you may use a simple ViewController to show the label 'Loading...' and then create the MainViw or next view which you want show to user in the end, when it was done, hide the first view show the MainView on screen...

Better do this in a thread from viewDidLoad and call your method in a thread, before calling the thread show your label and when the data is loaded through the thread hide the label.
you can do it like this.
-(void)viewDidLoad{
[self showLabel];
[NSThread detachNewThreadSelector:#selector(loadData:) toTarget:self withObject:nil];
}
Update
- (void) loadData:(id)object{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Network Operations
//...
//...
//...
//...
[self performSelectorOnMainThread:#selector(reloadView:) withObject:arg waitUntilDone:[NSThread isMainThread]];
[pool release];
}
Hope this helps.
Thanks,
Madhup

Related

Presenting a ModalViewController inside a ModalViewController

I have a view which is presented as a modal view controller which takes username and password credentials. I want this view to check the delegate, and if the user hasn't previously set an unlock pin for the app, to then show the change pin view as a modal view controller. This is my code...
+(void)presentCredentialsViewController:(UIViewController *)vc{
CredentialsViewController *cvc = [[CredentialsViewController alloc] init];
[vc presentModalViewController:cvc animated:FALSE];
}
and then in CredentialsViewController
-(void)viewDidLoad{
[super viewDidLoad];
if([appDelegate.pin isEqualToString: #""]){
UserPrefsViewController *upvc = [[UserPrefsViewController alloc] init];
upvc.cancelButton.hidden = true;
[self presentModalViewController:upvc animated:FALSE];
}
}
But for some reason it doesn't work. The debugger steps through the code without error, never the less, the second modal view controller isn't displayed.
First, I would suggest checking that your appDelegate.pin is blank and not nil. If it is nil, the if statement would not be satisfied and your second ModalView would not be presented.
You may also want to try the previous suggestion, calling presentModalViewController from viewDidAppear, or setting a delay if leaving it in viewDidLoad. It is possible that the CredentialsViewController is trying to present the second view when it has not yet presented itself.
The if statement is being hit and the second PresentModalViewController is executing without error, but it just wasn't displaying. I did try putting the code in ViewDidAppear and a load of other places as well, such as applicationWillBecomeActive etc. Although not actually crashing the code, still none of these approaches would show the view controller. In the end I have opted for this:
start with pin of #""
on applicationDidEnterBackground check if pin has been set
if yes
PresentModalViewController: PinViewController
if no
do nothing
Bit of a hack but it will do for now. I suppose I should put some sort of notification in somewhere warning that the pin hasn't been set. The suggestion about the delay may possibly work I suppose. I might give it a go in the future. Thanks guys....points up!

Scrolling exits CFRunLoopRun on iPhone

I've created a UIView, and I'm showing it as modal dialog by using CFRunLoopRun.
Everything works fine, but when the user does any kind of scrolling in the UIView, it exits the CFRunLoopRun.
I've read about this issue but didn't find a solution.
Any idea?
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[customdialog show]; // my method to show the dialog
CFRunLoopRun(); //exits CFRunLoopRun when scrolling on customdialog (UIView)
[pool release];
Scrolling causes the runloop mode to change, and this is probably causing CFRunLoopRun() to terminate. So you'd need to keep re-running CFRunLoopRun() repeatedly until you set some flag to stop.
But there's seldom any reason to do this. You can subclass UIAlertView, or you can simply make your dialog full-screen so that it prevents touches on the other view (that's how I always do this). Just create a transparent, full-screen view, and put your dialog in it, and add it as a subview of the current view. Then there's no special futzing with the runloop.

crash when switching between tableviews using tab controller and MBProgressHUD

I have two tableviews. One loads when I select one tab, and the other loads when I select the other tab.
I use MBProgressHUD to allow for quick switching between the tabs as I pull the tableview datasource from the web using Objective Resource and that can take a little bit. So I throw up a HUD progress indicator waiting for the data to load.
This in turn has allowed me to quickly switch between tabs. But.... If I do it quick enough an exception occurs
EXC BAD ACCESS
with NSZombiesEnabled I get this:
2010-08-02 22:44:43.116 Film Fest[962:8703] *** -[MBProgressHUD release]: message sent to deallocated instance 0x85490b0
In both my tableviews I use two different custom tableviewcells.
What should be my next step to debug this?
... so I have moved the code to create the HUD from my viewWillAppear: method to viewDidLoad: and the crash went away.
// The hud will dispable all input on the view (use the higest view possible in the view hierarchy)
HUD = [[MBProgressHUD alloc] initWithView:self.view];
//HUD.graceTime = 0.5;
//HUD.minShowTime = 5.0;
// Add HUD to screen
[self.view addSubview:HUD];
// Register for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(loadFilms) onTarget:self withObject:nil animated:YES];
however this doesn't really fix my issue as viewDidLoad only occurs once in a long while especially with the new multitasking. I need this HUD to fire the selector everytime the tableview appears.
Why is it not correct for me to have it occur in the viewWillAppear... is it because that can be loaded so much and I just kept on allocating the object? perhasp I should allocate in viewDidload and fire the
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(loadFilms) onTarget:self withObject:nil animated:YES];
only in my viewWillAppear:?
Thoughts?
Find all the places where you init/retain/release/autorelease an instance of MBProgressHUD.
The NSZombie tells that this project receives a release message after it is being already deallocated (retain count is zero).
It will help if you will post some code - otherwise it is too difficult to help you...
EDIT:
As you have mentioned in your edit, it would be much better if you'd initiate the HUD only once per view controller (e.g. in viewDidLoad) and call to showWhileExecuting each time you need it (e.g. in viewWillAppear).
But it still doesn't explain the crash.
You you execute the entire code you have posted (as is) for a few times from the same instance of a view controller then you should have memory leaks. That is because I don't see where you release the old instance of HUD.
In addition, you are better set the delegate of the HUD to be nil right before releasing it and before calling the [super dealloc]; in the dealloc method of the view controller.
The last one might cause the EXC BAD ACCESS error.
One more thing, if the entire code you have posted is executed more than once then you should also have few HUD views under the self.view.
I don't know what HUD does in the background - maybe this causes the error...

Multiple view controllers with their own xibs, and when they load

Going through many tutorials and with the help of everyone here, I am trying to wrap my head around using multi view controllers with their own xib files.
I have one example where there is a : multiViewController and two others: aboutViewController, rulesViewController.
In both aboutViewController.m and rulesViewController.m files, I have placed the following code:
- (void)viewDidLoad {
NSLog(#"rules View did load"); // (Or About View did load, depending on the .m file)
[super viewDidLoad];
}
The mainViewController.m file contains:
-(IBAction) loadRules:(id) sender {
[self clearView];
[self.view insertSubview:rulesViewController.view atIndex:0];
}
-(IBAction) loadAbout:(id) sender {
[self clearView];
[self.view insertSubview:aboutViewController.view atIndex:0];
}
My question is, why is it when I run the application does the ViewDidLoad for both About and Rules fire? Meaning, I get the NSLog messages. Does this mean that regardless of the separate xib files, all views are loaded on start up?
My point of all this is: I want the multiViewController to be my main menu which will have separate buttons for displaying the other xib files when clicked. I had been placing my "initalize" code in the viewDidLoad but this seems wrong now as I don't want to hit until the users presses the button to display that view.
An example was to have a view that is: playGameViewController. In the viewDidLoad I was checking to see if a prior game was in progress and if so, prompt the user if they would like to resume. In the above case, when the app starts, it prompts right away (because the viewDidLoad is firing) even though I only wanted to display the main menu first.
Any explanation is greatly appreciated. Thanks in advance.
Geo...
My question is, why is it when I run
the application does the ViewDidLoad
for both About and Rules fire?
Meaning, I get the NSLog messages.
Does this mean that regardless of the
separate xib files, all views are
loaded on start up?
When you call
[self.view insertSubview:rulesViewController.view atIndex:0];
it's going to first call viewDidLoad for the initial view and then viewDidLoad once again for RulesViewController.
When your MainViewController, or any view for that matter loads, viewDidLoad is called automatically. ViewDidLoad is there in order for you to override or modify any objects in the nib, or you can create objects yourself. Views are only loaded on an as needed basis. If you were to load all your views initially when the app boots up, the user would just see a black screen until all the views are processed.
You say you are going through some tutorials, I don't know your area of expertise yet, but have you looked into navigation based apps using UINavigationController?
Just an example as your request if you want to have a button load a view you can do something like.
- (IBAction)pushSecondView:(id)sender {
SecondViewController *secondView = [[SecondViewController alloc] initWithNibName:#"SecondViewController" bundle:nil];
if (secondView != nil) {
secondView.title = #"Second View";
[self.navigationController pushViewController: secondView animated: YES];
}
}

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)