Why does viewDidLoad wait before updating the UI? - iphone

I'm just trying to get my head around the Objective-C event model on iPhone, and by the looks of things I have fundamentally misunderstood something here.
For the purpose of experimentation, in a view controller's -viewDidLoad method, I am setting a UILabel's text, then sleeping for two seconds, and then changing the label's text again.
My expectations are as follows: the label will first read, "First Text", then two seconds later it will be updated to read, "Second Text." Of course, this isn't quite how it happens. Instead, the view isn't visible at all for two seconds, and finally when it becomes visible its label reads, "Second Text."
Could somebody please explain to me what is going on? I'm interested to find out how you guys would achieve what I'm going for here.
Cheers.
UPDATE 1: Here's the viewDidLoad method:
- (void)viewDidLoad {
[super viewDidLoad];
label.text = #"First Label";
sleep(2);
label.text = #"Second Label";
}
UPDATE 2: I made a silly mistake here, so please ignore this update.
UPDATE 3: I have now added the following to my viewDidAppear method:
- (void)viewDidAppear: (BOOL)animated {
[super viewDidAppear: animated];
label.text = #"First Label";
sleep(2);
label.text = #"Second Label";
}
Unfortunately I'm having exactly the same problem.
UPDATE 4: Following gerry3 and Felix's suggestions, I have now implemented a performSelector, and boom! Works a treat!! I'm going to have to give it to gerry3 though as he certainly put the most amount of effort into helping me out. Thanks for all your contributions!

I guess the reason is that viewDidLoad runs on the mainThread as do all UIKit interactions. The UI can only be updated on the mainThread hence if you block the viewDidLoad with sleep(2) you are putting the mainThread to sleep and halt all userinterface updates on that thread.
Use NSTimer if you want to update the UI after a certain amount of time, rather than using sleep(2). Or use performSelector:withDelay: on self to perform a method later without blocking the mainThread.
Same holds true for viewDidAppear and viewWillAppear. Both run on the mainThread.

Put your code that modifies the view in viewDidAppear:.
The view is not yet visible when viewDidLoad and viewWillAppear: are called.
UPDATE
Just to be clear, I agree with the others that the correct way to do this is with a delayed method call or timer.
UPDATE 2
Here is the code that I am suggesting:
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"View will appear!");
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"View did appear!");
label.text = #"First Label";
sleep(2);
label.text = #"Second Label";
}
And the "correct" way:
- (void)viewDidLoad {
[super viewDidLoad];
label.text = #"First Label";
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(#"View will appear!");
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSLog(#"View did appear!");
[self performSelector:#selector(changeLabelText) withObject:nil afterDelay:2.0f];
}
- (void)changeLabelText {
label.text = #"Second Label";
}

I do not think you really want to be calling sleep in that method. This is something you should try and use a Timer for to avoid blocking the UI and the whole application. A Timer will let you call some code either just one or repeatedly. See the guide for more info

The signature of the viewDidAppear and viewWillAppear are wrong in your sample..they should be
- (void)viewWillAppear: (BOOL)animated {
NSLog(#"View will appear!");
}
-(void)viewDidAppear : (BOOL)animated {
NSLog(#"View did appear!");
}
The frameworks will not call implementations with the wrong signature as the message dispatch does not find them.

Related

call viewDidDisappear in Tabbar based Application

I am using tab bar based iPhone application.I have set NSTimer in first two tabs.i want to invalidate this timer. so i am invalidating in viewDidDisappear. But when i am clicking on different tab, it will never call viewDidDisappear. I don't know how to call this method?please help me...thanking you in advance...
are you put [super viewWillDisappear:animated]; in viewWillDisappear some time it happaned becouse we forget to put this line in to viewWillDisappear
- (void) viewWillDisappear:(BOOL) animated {
[super viewWillDisappear:animated];
}
set time invalid in viewWillDisappear: method , like bellow..
-(void)viewWillDisappear:(BOOL)animated
{
[yourTimer invalidate];
yourTimer = nil;
}

Irregular behavior using the viewDidAppear and viewDidLoad methods

I am trying to update some of my views when they appear, so I naturally found myself using the viewDidAppear: and viewWillAppear: methods. However, I have experienced two problems with using these methods:
When I only implement one of the methods, the changes that I am looking to make are not completely there, so in order for everything to work, I implemented both methods with the same code.
Even after implementing both methods with the same code, there is a 0.5 to 1 second delay when updating the view's content.
Here is my code for my custom made table view controller:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView reloadData];
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableView reloadData];
}
For some reason, I must call the reloadData method twice to completely update my table view.
Here is my code for my custom made normal view controller:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
navItem.title = #"Name1";
nameLabel.text = #"Name1";
nameField.hidden = YES;
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
navItem.title = #"Name1";
nameLabel.text = #"Name1";
nameField.hidden = YES;
}
Thank you!
You should only use the viewWillAppear method.

The keyboard make the UIView slowly when it comes up?

i have an action that allows me to present a ModalViewController and show the UITextField as a first responder, the problem is when this ModalViewController will come up it takes a little time, the cause is the keyboard, and when i grab the code to the viewDidAppear the keyboard take a little time to show up, so how can i do to make the UIViewController comes up quickly?
- (IBAction)goToModalViewController
{
ModalSearchViewController *msvc = [[ModalSearchViewController alloc] init];
self.msvc.context = context;
self.msvc.delegate = self;
[self.msvc setModalTransitionStyle:UIModalTransitionStyleCrossDissolve ];
[self presentModalViewController:msvc animated:YES];
}
The viewWillAppear of the ModalViewController:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
[findTextField becomeFirstResponder];
}
Try like this in the viewWillAppear.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
//[findTextField becomeFirstResponder];
[findTextField performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.3];
}

What is the right place to add a subview dependent on scrollView.contentSize?

I'm using Cocoanetic's pull-to-reload but with a twist: I would like the UITableView to be able to pull up, as it were, to load more data.
I customized the classes and have managed to adjust all functionality to support this. What got me stumped, basically, is where in my code to create and add the extra view.
I first tried to add it in viewDidLoad of Cocoanetic's class (a UITableViewController):
- (void)viewDidLoad
{
[super viewDidLoad];
refreshHeaderView = [[EGORefreshTableHeaderView alloc] initWithFrame:CGRectMake(0.0f, 0.0f - self.view.bounds.size.height, 320.0f, self.view.bounds.size.height)];
refreshFooterView = [[EGORefreshTableFooterView alloc] initWithFrame:CGRectMake(0.0f, 0.0f + self.tableView.contentSize.height, 320.0f, 20.0f)];
[self.tableView addSubview:refreshHeaderView];
[self.tableView addSubview:refreshFooterView];
self.tableView.showsVerticalScrollIndicator = YES;
}
This does not work, as self.tableView.contentSize.height is zero at this point, because the table hasn't loaded it's data yet.
Not to worry, I thought, and tried to add it in the viewDidLoad of the UITableViewController subclass I made:
- (void)viewDidLoad
{
[super viewDidLoad];
// stuff
self.model = [ListingModel listingModelWithURL:#"avalidurl" delegate:self];
refreshFooterView = [[EGORefreshTableFooterView alloc] initWithFrame:CGRectMake(0.0f, 0.0f + self.tableView.contentSize.height, 320.0f, 20.0f)];
[self.tableView addSubview:refreshFooterView];
}
Note I set the model first, but that also didn't work, for the same reason. I assume the table hasn't been layed-out yet. In frustration I gave my class a BOOL property and an addFooter method (the BOOL to make sure it's only called once) called from tableView:cellForRowAtIndexPath: which obviously is a far cry from The Right Way™
So what would, given this scenario, be The Right Way™?
The solution was easier than I thought and 7KV7 actually gave me the hint I needed.
- (void)viewDidLoad
{
[super viewDidLoad];
// stuff
self.model = [ListingModel listingModelWithURL:#"avalidurl" delegate:self];
/*
We're forcing a reload of the table here, that way the table has a
contentSize, so adding the footerView now works correctly.
*/
[self.tableView reloadData];
[self addRefreshFooter];
}
From this previous SO question Get notified when UITableView has finished asking for data? subclassing UITableView's reloadData is the best approach :
- (void)reloadData {
NSLog(#"BEGIN reloadData");
[super reloadData];
NSLog(#"END reloadData");
}
reloadData doesn't end before the table has finish reload its data. So, when the second NSLog is fired, the table view has actually finish asking for data.
If you've subclassed UITableView to send methods to the delegate before and after reloadData. It works like a charm.

presentModalViewController in viewDidLoad on first launch

I've been searching around but unfortunately have had no luck.
My app requires the user to sign in/sign up the first time he or she launches the app. I know how to determine first launch (using NSUserDefaults) but whenever I try to present the modal containing the sign in/ sign up controls, nothing happens.
Here's what I have:
-(void)viewDidLoad {
[self showLogin];
[super viewDidLoad];
}
-(void)showLogin {
FlipsideViewController *controller = [[FlipsideViewController alloc] initWithNibName:#"AccountView" bundle:nil];
controller.delegate = self;
controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:controller animated:YES];
[controller release];
}
However, nothing happens. The main view just loads as normal. Any help is greatly appreciated.
-Giles
[UPDATE]
Fixed simply by using..
-(void)viewDidAppear:(BOOL)animated
{
}
instead of
-(void)viewDidLoad
{
}
Thanks anyway!
/idiocy
I had the same issue and ended up using viewDidAppear as well. The only problem with the viewDidAppear approach is that if you load other UIViewControllers on top, then reshow the base, then your setup code gets called over and over. I ended up having to add a boolean value (initialised to YES) to this view controller and check that value before deciding what to do. Hope this helps someone...
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:(BOOL)animated];
if(justLaunched)
{
justLaunched = NO;
if(settingsFileExists)
{
[self displayMainView];
}
else
{
[self displaySetupView];
}
}
}
How about using performSelector:withObject:afterDelay in the viewDidLoad function? That's how I do it, with a short delay of 0.1s.
And invoking this in the viewDidLoad isn't very safe : the sequence viewDidLoad / viewDidUnload can occur at runtime when the iPhone needs to release some views in order to get back some free memory.
The side effect of such sequence would be that your login controller would be shown...
As you said the viewDidAppear looks better but not simply put it at the end of the appDidFinishedLaunching the delegate of your UIApplication?