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.
Related
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)
I am making an application using two view controlers. When I am working on my first view I have posibility to go to another view using button "Settings" and method conected to this button looks like this:
-(IBAction)Settings:(id)sender{
[self presentModalViewController:settingsHandle animated:YES];
settingsHandle is an object of a second view class which is alloceted when the first view is loaded.
My problem starts while I am in a second view and i call method which include NSTimer object. This method is working during hmmm lets say 30 sec but it also can be 5 min, the result of this method is calling onother view the 3rd one.
Everything goes fine while am waiting for the result of this function in a second view.The result is that i am in a 3rd view.
When, during the method is working I am going to the first view from the second (using [self dismissModalViewControllerAnimated:YES]; )I can see that the method has finished(using NSLOG) but [self presentModalViewController:thirdview animated:YES]; is not working, just nothing happens.
so to sum up:
Waiting for the result in a secodnview (Succes third view uploaded)
Waiting for the result in a firstview (fail nothing happens)
And my goal is to make it happens from the firstview!
You can't present a ModalViewController from a ViewController that is dismissed. So, you need to keep track of the visible ViewController and call presentModalViewController from there. In your scenario, the easiest solution would be to make your NSTimer invoke a method in your first ViewController which goes like this
- (void)presentViewController:(NSTimer *)timer
{
if(self.modalViewController == nil)
[self presentModalViewController:settingsHandle animated:YES];
else
[self.modalViewController presentViewController];
}
If you create the NSTimer in your 2. ViewController, you would of course need a reference to the first ViewController. You could just pass this reference like this
-(IBAction)Settings:(id)sender{
settingsHandle.myParentViewController = self; //You need to create this var in settingsHandle
[self presentModalViewController:settingsHandle animated:YES];
//...
}
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
I'm trying to make a modal view which displays the champion of my app.
there's a NSMutableString variable called champ in modal view,
which is supposed to be updated by returnChamp function in main view.
the champ string is correctly set in main view,
but in modal view, the champ value appears as (null).
In fact, it seems it doesn't even go into the returnChamp function.
so apparently something wrong with my calling or implementing returnChamp,
but I have another function that does the similar, and that works fine.
could anyone please help me?
-(void) mainView{
.....
champ = [[currentPlayers objectAtIndex:playerIndex] retain];
NSLog(#"%#",champ);
modalWinner = [[winner alloc] init];
modalWinner.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:modalWinner animated:YES];
}
- (NSMutableString *) returnChamp{
NSLog(#"returnChamp");
return champ;
}
//in modalWinner
-(void) modalView{
..............
champName = [[NSMutableString alloc] init];
NSLog(#"%#", [(MainViewController *)self.parentViewController returnChamp]);
champName = [(MainViewController *)self.parentViewController returnChamp];
UIImage *champImage = [UIImage imageNamed:champName];
}
self.parentViewController is probably not actually a reference to your object. For some reason, it seems that the framework always insists on setting a UINavigationController as self.parentViewController - even for modals, and to the extent that it will create one if there isn't already one. This is probably going unnoticed because you're casting it to your MainViewController type.
You'll need to find a different way of making your original object available to be communicated with, or perhaps pass the appropriate value to the newly-instantiated controller before you present it.
For example, if you add a champName property to the modal class, you can do:
modalWinner = [[ModalWinnerViewController alloc] init];
modalWinner.champName = myValue; /* Set value before presenting controller */
[self presentModalViewController:modalWinner animated:YES];
There will probably be some code needed to update the UI with this value. The viewWillAppear method of the modal view controller is a good place for this as it is called by the framework immediately before the view is presented.
Note that this property-based approach could be used to keep a reference to your intended parent object, as well. And see here for a different approach to solving a similar problem.
Is it possible to select a row in a previous UITableview.
I provided image samples to get a more clear picture of what is happening exactly.
http://img186.imageshack.us/img186/933/picture8a.png
http://img405.imageshack.us/img405/9327/picture9d.png
http://img200.imageshack.us/img200/5386/picture10thm.png
At the morning workout screen , one can select a row and behind it so it will play a Movie.
If the movie plays you can press the secondbutton and it will take you to the final table.
If you press the backbutton you will simply return to the previous screen.
Now here is where my problem lies.
If i'm in my final screen after pressing the secondbutton and I press on the backbutton it would be great if it could play the previous video( in other words the video connected to the previously selected cell)
So if it's possible for me to create a function or some sort of action that can actually select the previously selected row from the first field ( for instance Lunge Forward in this example ).
perhaps with some like
previousRow = [ self.tableView indexPathForSelectedRow];
[self tableView:[self tableView] didSelectRowAtIndexPath:previousRow];
just doing something there
Even if it's not possible I would appreciate it if someone would let me know.
edit:
Here is some code behind the cell when it's going to play a video
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Get the dictionary of the selected data source.
NSDictionary *dictionary = [self.tableDataSource objectAtIndex:indexPath.row];
//check to see if video must be played
NSString *playVids = [dictionary objectForKey:#"playVids"];
finalscreen = [dictionary objectForKey:#"finalScreen"];
if(playVids == nil )
{
if(CurrentLevel != 0 )
{
if( finalscreen == nil )
{
NSString *movies = [dictionary objectForKey:#"movieID"];
NSURL *movie = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:movies ofType:#"m4v"]];
theMovie = [[MPMoviePlayerController alloc] initWithContentURL:movie];
[theMovie setOrientation:UIDeviceOrientationPortrait animated:NO];
[theMovie setScalingMode:MPMovieScalingModeAspectFit];
theMovie.backgroundColor = [ UIColor whiteColor];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(myMovieFinishedCallback:)
name:MPMoviePlayerPlaybackDidFinishNotification
object:theMovie];
[theMovie play];
[self getToolBarStuff];
lblTitle.text = [dictionary objectForKey:#"Title"];
[[[UIApplication sharedApplication]keyWindow]addSubview:toolbar2];
[[[UIApplication sharedApplication]keyWindow]addSubview:imageView];
[[UIApplication sharedApplication]keyWindow]addSubview:lblTitle];
}
}
else
{
WebViewController *web = [[WebViewController alloc] initWithNibName:#"WebView" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:web animated:YES];
[web release];
}
}
else {
//Prepare to tableview.
rvController = [[RootViewController alloc] initWithNibName:#"RootViewController" bundle:[NSBundle mainBundle]];
//Increment the Current View
rvController.CurrentLevel += 1;
//Set the title;
rvController.CurrentTitle = [dictionary objectForKey:#"Title"];
//Push the new table view on the stack
[self.navigationController pushViewController:rvController animated:YES];
rvController.tableDataSource = Children;
[rvController release];
}
}
I get my info/feeds from a dictionaryfile and also different tags in my dictionary tell my tableview when to play the video and when not( playVids ).
so to be exact I have a start screen which brings you to a second screen and that one brings you to the morning workout screen and from there if you click a cell it will play a (different with every cell) video. I can't use the standard design from apple(with the backbutton above) since this assignment specifically asks to use their own design.
That's the reason why i created my own tabbar and backbutton.
My backbutton looks like this
-(void)back_clicked:(id)sender
{
[self.navigationController popViewControllerAnimated:YES];
CurrentLevel--;
switch( CurrentLevel ) {
case 0;
[toolbar removeFromSuperView];
default:
break;
}
}
So if I'm in the last screen I just pop that screen to return to my previous one( the morning workout). And what actually has to be done is pop that screen and play the last video. I thought I could do that by popping the last screen and then selecting the previously clicked cell in morning workout.
Any thoughts on that.
I am not sure I understand your problem exactly here. Can you show a bit more code? You have 3 view controllers, right? Are you using a standard navigation controller pattern with push/pop to get from one to the other? It looks like maybe you aren't since the buttons are in an odd layout.
If you are then pressing the back button can just pop view 3 and go back to view 2 naturally (it will still be there, so the video should still be playable) so I don't see why you need to mess with the table in view 1 in order to find that and make it show again. You should just be able to hook into viewWillAppear and do the same thing you did when coming from view 1.
And if you aren't using a standard navigation controller pattern then maybe this is the problem. From a UI perspective you probably should do that anyway - people will expect a standard back button in the title bar.
I am pretty sure your problem is soluble anyhow - you can always pass object references from one view to the other when creating them for example - but I don't think you need to resort to the method you have in mind.
edit: reading the additional detail I see you are indeed using push/pop. So you have a stack of views, and when one is popped it uncovers the one beneath it, right? It should therefore be possible to do what you want by implementing the viewWillAppear or viewDidAppear methods in your controller. When your final screen is popped, if I understand correctly it will uncover the view which is able to play the movie. Your viewcontroller will get the viewWillAppear / viewDidAppear messages in that case - by maintaining state and receiving those messages you can then implement logic to decide whether or not you need to play the movie. Rather than call the didSelectRow. method again you could perhaps put the movie play stuff in its own method so you can call from either viewDidAppear or didSelectRow...
I also am not sure that I fully understand your question either but it sounds similar to a problem I was working on recently. Check out this post on using up down arrows to move through the parent table while in a detail view. My app was based on the same sample code so you should be able to get this to work without much trouble.
Up/down arrows in nav bar of detail view to page through objects in parent table