ViewDidLoad method calling behavior [duplicate] - iphone

This question already has an answer here:
Method calling via performSelectorOnMainThread Vs Normal method calling
(1 answer)
Closed 9 years ago.
In my viewDidLoad, I call a function:
[common startActivityIndicatorOnView:self.view];
This method adds a view with Activity indicator, in the center of self.view.
My current view is pushed on a Navigation Stack. This is how the view looks after this method returns (the activity indicator view is not in center):
However, if I call the same method this way:
[common performSelectorOnMainThread:#selector(startActivityIndicatorOnView:) withObject:self.view waitUntilDone:NO];
The view looks like the following image (the activity indicator view is in center):
I do not get, How does it make a difference if the calling line is written in viewDidLoad.
If any one can help me get this, thanks in advance.
Just for reference,
the method looks like this:
-(void) startActivityIndicatorOnView:(UIView *)view {
if ([NSRunLoop currentRunLoop] != [NSRunLoop mainRunLoop]) {
[self performSelectorOnMainThread:#selector(startActivityIndicatorOnView:) withObject:view waitUntilDone:NO];
return;
}
view.userInteractionEnabled = NO;
activityBgView = [[UIView alloc] initWithFrame:CGRectMake((view.frame.size.width/2) - 50, (view.frame.size.width/2) - 50, 100, 100)];
activityBgView.center = view.center;
activityBgView.backgroundColor = [UIColor grayColor];
activityBgView.alpha = 0.8;
spinner = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake((activityBgView.frame.size.width/2)-10, (activityBgView.frame.size.width/2)-10, 20, 20)];
spinner.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
spinner.center = view.center;
[view addSubview:activityBgView];
[view addSubview:spinner];
[spinner startAnimating];
}

ViewDidLoad is called when you load viewControllers (via nib/xib or created programmatically in the loadView method), meaning xcode create all views and instantiate and alloc all objects in xib...
but, your viewController view (and subViews) are still not added in any view...
yourViewController.view.superView = nil;
so, its view has got the frame that you set in xib, but if you tell it to resize inside its superview, when you add it (e.g. with a push or an addsubview), its frame changes, but your
spinner won't change its position.
calling a performSelectorOnMainThread just will call your method later, when your current thread step ahead and may have pushed your viewController.view, so, when executed, yourViewController.view.superView exists, and so view.frame has already changed.
try to move your call to
[common startActivityIndicatorOnView:self.view];
in a viewWillAppear method: at that point yourViewController.view should been already resized to fit its superView
EDIT:
# pavel question:
after what moment yourViewController.view.superView will be not nil?
banally: when you add its view to a view. that is, firts you allocate and init it (init with a nib or via code)
something like:
yourViewControllerInstance = [[YourViewController alloc]initWithNibName:#"yourViewControllerNib" bundle:nil];
at this point the method viewDidLoad in your class is called (but yourViewController.view.superview 0 nil)
later, you usually use your new viewController, so you "push" it in the stack of a navigationController, or you just add its view to the current viewController.view... something like:
[currentViewController.view addSubview:yourViewController.view];
after this line, as you may imagine, yourViewController.view.superView = currentViewController.view, and the method viewWillAppear of yourViewController is called, so you can use superView inside it.
Notice that at this point your viewController.view is still not visible on screen, so you can adjust sizes, move or add views here without users see any changes.
after this, yourViewController will show, and at the end, and the method viewDidAppear of yourViewController is called (for any other code, in case)

Related

Creating an UIImageView on UIButton click

I have a button setup in one class and on clicking it, I am calling a method which is in another class and in that method I have setup code for UIImageView.
the method is getting called, But somehow the ImageView doesnt seems to get created, I dont understand why, And therefore I cannot add any Image to that ImageView.
Here's some code of what I am trying to do:
In my first viewControllerClass I have this method which fires on a button click:
- (IBAction)buttonClicked:(id)sender {
[secondViewController createNewImageView];
}
and then in the second viewController I am calling that createNewImageView method, in which I am creating an ImageView at runtime:
-(void)createNewImageView {
newImageView = [[UIImageView alloc] initWithFrame:CGRectMake(100,100, 100, 100)];
newImageView.backgroundColor = [[UIColor yellowColor] colorWithAlphaComponent:0.4];
[self.otherNewView addSubview:newImageView];
}
But somehow the ImageView is not getting added on my view, Any Ideas why this is happening ?
What am i missing here ..?
Thanks for your time :)
EDIT : The class which holds the button is UIPopOverController.
Have you allocated secondViewController before doing,
[secondViewController createNewImageView];
I think you have not created an object for secondViewController.
So before calling the function create the object for the class.

How to update a progressbar during instantiation of timeconsuming ui elements?

I would like to update a progressbar while i am instantiating some ui elements that takes some times. I first create my view during the viewLoad method and add my progress bar there. Once my view appeared in the viewDidAppear method I am making several uikit objects instantiation but i would like to update the progress bar in the mean time. I am not sure how to proceed since everything should happen in the main thread as it is ui elements.
Here is part of my code:
-(void) viewDidAppear:(BOOL)animated
{
// precompute the source and destination view screenshots for the custom segue
self.sourceScreenshotView = [[UIImageView alloc] initWithImage:[self.view pw_imageSnapshot]];
[self.progressBar setProgress:.3];
SCLViewController *rvc = [[SCLViewController alloc] init];
UIView *destinationView = rvc.view;
destinationView.frame = CGRectMake(0, 0, kWidthLandscape, kHeightLandscape);
self.destinationScreenshotView = [[UIImageView alloc] initWithImage:[destinationView pw_imageSnapshot]];
[self.progressBar setProgress:.5];
}
In the above code I just need to create two screenshots of views to use them later on. The problem is that i only see the last update (.5) when setting the progress to the progress bar. What is the proper way to do this update?
You can use the performSelectorInBackground:withObject: method in order to instantiate your heavy views. That method (the one that instantiates your views) will have to set your progress bar progress in the main thread.
So your code would look something like this:
- (void)viewDidAppear:(BOOL)animated
{
[self performSelectorInBackground:#selector(instantiateHeavyViews) withObject:nil];
}
- (void)instantiateHeavyViews
{
self.sourceScreenshotView = [[UIImageView alloc] initWithImage:[self.view pw_imageSnapshot]];
[self performSelectorOnMainThread:#selector(updateMyProgressView:) withObject:[NSNumber numberWithFloat:0.3f] waitUntilDone:YES];
SCLViewController *rvc = [[SCLViewController alloc] init];
UIView *destinationView = rvc.view;
destinationView.frame = CGRectMake(0, 0, kWidthLandscape, kHeightLandscape);
self.destinationScreenshotView = [[UIImageView alloc] initWithImage:[destinationView pw_imageSnapshot]];
[self performSelectorOnMainThread:#selector(updateMyProgressView:) withObject:[NSNumber numberWithFloat:0.5f] waitUntilDone:YES];
}
- (void)updateMyProgressView:(NSNumber *)progress
{
[self.progressBar setProgress:[progress floatValue]];
}
Edit: of course, it won't animate your progress bar (I don't know if that is what you wanted). If you want it to move on while your views are being created, you should use a delegate to be notified of the progress, and this can be a bit harder. This way you would be able to update the progress bar every time the delegate is notified.

Pushing View Controller - viewDidAppear not called

I have this piece of code to push a view controller:
// Setup the animation
[self.navigationController pushViewController:self.productView animated:YES];
self.productView.imageURL = [product imageURL];
// Set the title of the view to the product's name
self.productView.title = [product name];
// Set the label text of all the labels in the view
[self.productView.caloriesL setText:[product calories]];
[self.productView.fatL setText:[product fat]];
[self.productView.saturatesL setText:[product saturates]];
[self.productView.sugarL setText:[product sugar]];
[self.productView.fibreL setText:[product fibre]];
[self.productView.saltL setText:[product salt]];
But the delegate method viewDidAppear does not get called when the productView appears. I looked up the problem on google and theres a lot of different solutions, none of which I could apply to my problem.. I had a similar problem in a previous solution but I got around it by manually calling viewDidApear in the viewDidLoad method. Unfortunately in this case I can't do that as viewDidLoad is called only once (on the first push). Does anyone know how to fix this?
Thanks,
Jack Nutkins
EDIT:
Here is the viewDidAppear method in the productView (and selector):
- (void)viewDidAppear:(BOOL)animated{
//Start animating the activity indicator
[indicator startAnimating];
//Perform this method in background
[self performSelectorInBackground:#selector(loadImage) withObject:nil];
}
- (void) loadImage {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Load the animals image into a NSData boject and then assign it to the UIImageView
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]];
UIImage *image = [[UIImage alloc] initWithData:imageData];
self.imageView.image = image;
//Stop animating the activity indicator
[indicator stopAnimating];
[pool drain]; //see comment below
}
First: You definitely don't want to be calling any of the standard viewWillLoad, viewDidLoad, viewWillAppear, etc. methods manually. Let the OS do it for you.
Second: Can you show us how your viewDidAppear method is implemented in your self.productView instance? (Just a hunch, you're not expecting this method to be called on your navigation controller, right?) I just want to make sure your method signature is exactly correct. If it's not (due to a mispelling, improper args, etc.) then it definitely won't be called.
Third: I would move your pushViewController: call to after the rest of the code you provided. You don't want the view to be pushed on the screen (so the user can see it) and then have a bunch of on-screen values immediately change. Set your ivars and title property first, then push the view controller. This eliminates any weird flickering.
I solved it, though it doesn't seem conventional, can't believe I didn't try it earlier :
I put this line :
[self.productView viewDidAppear:YES];
Underneath :
// Setup the animation
[self.navigationController pushViewController:self.productView animated:YES];
I also moved the code to set the labels text to run before the above line. (As well as changing my code to send strings to the pushed controller rather that accessing its UI elements.)
Thanks for everyones help,
Jack

Why aren't my UILabels refreshing with new information after an NSNotificationCenter update?

I am working on a weather app, and have everything working perfectly...Except the UILabels that are displaying the data. Once the app loads for the first time, it goes through and finds the data correctly and then displays it.
This is what one of the UILabels looks like, inside my main RootViewController:
UILabel *myCityLabel = [[UILabel alloc] initWithFrame:CGRectMake(10, 30, 200, 80)];
[myCityLabel setText:[NSString stringWithFormat:#"%#",placemark.locality]];
myCityLabel.textAlignment = UITextAlignmentLeft;
myCityLabel.textColor = [UIColor blackColor];
myCityLabel.font = [UIFont fontWithName:#"Helvetica" size:24];
myCityLabel.backgroundColor = [UIColor clearColor];
[self.view addSubview:myCityLabel];
[myCityLabel release];
I have CoreLocation running in the background. Inside my appDelegate, I see these two methods get called once the app is invoked again (after closing it):
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"applicationDidBecomeActive:");
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(deviceNotificationReceived:) name:UIApplicationDidBecomeActiveNotification object:nil];
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
lbsViewController = [[LBSViewController alloc] init];
[lbsViewController viewDidLoad];
}
- (void)deviceNotificationReceived:(NSNotification *)notification
{
NSLog(#"was this received?");
}
What I am trying to do here is to launch the viewDidLoad method of lbsViewController (my main RootViewController. I can see via the console that it is returning new information, and even that the function viewDidLoad is being called, but the labels aren't refreshing with the new data...any suggestions on what route(s) I can take to fix this problem?
I should note that the only time the UILabels are refreshing with new data is upon building the app from Xcode to my device.
You say that you solved the problem by removing multitasking. Your problem was that the NSNotification you sent from the background thread arrived on the same background thread, and you tried to update your UILabel from that background thread. That isn't allowed - you must update UI elements from the main thread.
To solve this, you can marshall the call to the main thread using:
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
so, something like this (in your notification handler):
[viewController.outputLabel performSelectorOnMainThread: #selector( setText: ) withObject: currentChar waitUntilDone: YES];
note, this is the same response I gave to this question: How do I display characters to a UILabel as I loop through them?
The two lines:
lbsViewController = [[LBSViewController alloc] init];
[lbsViewController viewDidLoad];
Are allocating a brand new LBSViewController and calling viewDidLoad on it. Probably you want to call viewDidLoad on your existing lbsViewController object rather than allocating a new one (ie, remove the first of those two lines).
In any case, it'd be better to have the view controller observe the notification itself and deal with everything internally. This sort of overloads the meaning of viewDidLoad, and adding a method with an alternative name arguably distributes the logic in an odd way.

uniimageview not updating after views tranistion

I have one main view where I display an image, in the method viewDidLoad:
ballRect = CGRectMake(posBallX, 144, 32.0f, 32.0f);
theBall = [[UIImageView alloc] initWithFrame:ballRect];
[theBall setImage:[UIImage imageNamed:#"ball.png"]];
[self.view addSubview:theBall];
[laPalla release];
Obviously, the value of posBallX is defined and then update via a custom method call many times in the same class.
theBall.frame = CGRectMake(posBallX, 144, 32, 32);
Everything works, but when I go to another view with
[self presentModalViewController:viewTwo animated:YES];
and come back with
[self presentModalViewController:viewOne animated:YES];
the image is displayed correctly after the method viewDidLoad is called (I retrieve the values with NSUserDefaults) but no more in the second method. In the NSLog I can even see the new posBallX updating correctly, but the Image is simply no more shown...
The same happens with a Label as well, which should print the value of posBallX.
So, things are just not working if I come back to the viewOne from the viewTwo... Any idea???????
Thanks so much!
You should use dismissModalViewControllerAnimated: to switch back to viewOne from viewTwo instead of trying to present viewOne modally.
Also note that viewDidLoad is called only once - after the view controller's view is loaded into memory. If you want to perform an action once a view comes back on screen, you should do so in viewWillAppear:.
Both of these points are discussed in the UIViewController class reference and in the View Controller Programming Guide.