I have 3 views.
I want to do the following:
A presents B modally
A dismisses B
A presents C modally
I have setup a delegate pattern where A is B's delegate. This is how I am presenting and dismissing in B:
[delegate dismissB]; //this is just [self dismissModalViewControllerAnimated:NO]
[delegate presentC]; //this is just [self presentModalViewController:c animated:NO];
For some reason my app crashes when I execute this code with no debugger results (I have NSZombieEnabled).
When I comment out [delegate presentC] the app will dismiss B properly. When I comment out [delegate dismissB] the app does nothing, even though the line executes. I am not sure why?
UPDATE:
Here is the code in A
-(void)showARView{
[self dismissModalViewControllerAnimated:NO];
ARViewController* arViewController = [[[ARViewController alloc] initWithNibName:#"ARViewController" bundle:nil]autorelease];
UINavigationController *arNavController = [[UINavigationController alloc] initWithRootViewController:arViewController];
LeaderBoardTableViewController* lbViewController = [[[LeaderBoardTableViewController alloc] initWithNibName:#"LeaderBoardTableViewController" bundle:nil]autorelease];
lbViewController.title = #"Leaderboard";
UINavigationController *lbNavController = [[UINavigationController alloc] initWithRootViewController:lbViewController];
arTabBarController = [[UITabBarController alloc] init];//initWithNibName:nil bundle:nil];
arTabBarController.delegate = self;
arTabBarController.viewControllers = [NSArray arrayWithObjects:arNavController, lbNavController, nil];
arTabBarController.selectedViewController = arNavController;
[arNavController release];
[lbNavController release];
[self presentModalViewController:arTabBarController animated:NO];
}
Here is the code in B
[delegate showARView];
When you call dismissB the delegate dismisses the viewcontroller. Under normal circumstances (if you do not retain it elsewhere) this leads to the viewcontroller to be dealloced synchronously. And afterwards you are trying to access the delegate instancevariable, but for this the code needs a sane (hidden) self pointer, which is dealloced. I'm not sure if NSZombie can help in this case. You can easily find out if this is the reason for your crash, by inserting [[self retain] autorelease]; before [delegate dismissB];. However this is a hack and not a fix. You have a design problem.
This is not the way delegates are meant to be used. B presents some userinterface and receives some user interaction. It should then tell A what has happened via a delegate message, e.G. bWasCanceled or bFinished. It is the duty of the delegate, in your case A, to decide what to do next. So in your case the delegate may decide to dismiss B and instead present C. Or in code:
// Inside A
- (void)controllerB:(UIViewController*)ctl didFinishWithResult:(id)something {
[self dismissModalViewControllerAnimated:NO];
// Instantiate and initialize c
[self presentModalViewController:c animated:NO];
}
// Inside B
[delegate controllerB:self didFinishWithResult:#"OK"];
If I completely misinterpreted your code, and everything is fine there, I have an other suggestion. I have seen strange issues when dismissing and presenting modal viewcontrollers in the same round of the runloop. You may try [delegate performSelector:#selector(presentC) withObject:nil afterDelay:0.0]; and see if that helps.
Related
I have a ViewController, VC1a, that presents VC2:
VC1a -> presentViewController: VC2
Is it possible to change VC1a into VC1b so that when dissmissViewControllerAnimated is called, it animates to VC1b instead of VC1a?
The reason I ask is because I want to return to a different screen without it animating back to VC1a. This relates to portrait/landscape changes.
Warning. This is potentially a bad/confusing UI choice for your users. But if you must...
You may be able to put VC1a inside a UINavigationController and modify the navigation stack while VC2 is in the foreground. Something like:
// in VC1a.m
[self presentViewController:VC2 animated:YES completion:^{
NSMutableArray *navigationStack = [[NSMutableArray alloc] init];
for (UIViewController *viewController in self.navigationController.viewControllers)
{
if (viewController != self.navigationController.viewControllers.lastItem)
{
[navigationStack addObject:viewController];
}
else
{
VC1b *viewControllerToSwapIn = [[VC1b alloc] init];
// probably some more initialization here
[navigationStack addObject:viewControllerToSwapIn];
}
}
self.navigationController.viewControllers = navigationStack;
}];
or possibly a better idea:
// in VC1a.m
[self presentViewController:VC2 animated:YES completion:^{
VC1b *viewControllerToSwapIn = [[VC1b alloc] init];
// probably some more initialization here
[navigationStack addObject:viewControllerToSwapIn];
[self.navigationController popViewControllerAnimated:NO];
[self.navigationController pushViewController:viewControllerToSwapIn animated:NO];
}];
OK, I implemented a Container view controller with two children - one for portrait, one for landscape and this took care of my problem.
I have spent days trying to figure out how to handle rotation correctly within iOS6 and I didn't realise there was a bug in rotating UITableViewController when pushed from a UINavigationController.
Thanks for the response #paulrehkugler - I was getting pretty desperate when I asked this question. There just doesn't seem to be any good examples of how to handle orientation changes while UINav/TableViewControllers are presented from a main view. I certainly wasn't wanting to implement such a hack.
I founded that the problem is the place where I'm calling the showNextView. I have another interface webService where i communicate with server and parse xml. When the parsing is finished with method parserDidEndDocument I'm calling the delegate method where is changed the view and show modal view. But when i call all that methods it will return to endDocument and xmlParseChunk and so on. It looks like the parserDidEndDocument is not realy the last method and somehow it mess with navigationcontroler. When i call the method for showig nextView with button it works.
The code which is working on button. In delegate method called from parserDidEndDocument is not working correct.
-(void)showNextView
{
UIViewController *nextView = [self.storyboard instantiateViewControllerWithIdentifier:#"vcTrabantInfo"];
[[nextView navigationController] setNavigationBarHidden:NO animated:NO];
[[self navigationController] pushViewController:nextView animated:YES];
UIViewController *picker = [[UIViewController alloc] init];
[picker setModalPresentationStyle:UIModalPresentationFormSheet];
[[self navigationController] presentModalViewController:picker animated:YES];
}
As usualy the problem was between keyboard and seat. The problem was that my modal views haven't been dismissed before i call another modal view :). So keep in mind that all is done in viewDidDisappear.
The app I'm making utilizes multiple views. such as a disclaimer view, a view to display answer so on and so forth.Up until now this is the code that I've been using to switch from one view to another
-(IBAction)swichtogain:(id)sender{
gainview *second = [[gainview alloc]initWithNibName:nil bundle:nil];
[self presentModalViewController:second animated:YES];
[second release];
}
I found this method in a tutorial, I was wondering, is this the best way to do it ? I use the same code to switch back n forth from one view to another for eg.
-(IBAction)swichtoview1:(id)sender{
view1 *view = [[gainview alloc]initWithNibName:nil bundle:nil];
[self presentModalViewController:view animated:YES];
[view release];
}
and when in view1 if the user hits the back button the following code gets executed
-(IBAction)swichtomainview:(id)sender{
mainview *view = [[gainview alloc]initWithNibName:nil bundle:nil];
[self presentModalViewController:view animated:YES];
[view release];
}
I haven't edited anything in the appdelegate files and this is a view based app. Does this method cause it to use more memory ? During the activity monitor test using the instruments , I noticed the memory usage gets higher every time I go from the main menu to another view and back to the main menu !. Is there a better way than this ?. Also one of the view is a calculator so when the user hits the calculate button it switches to the next view while changing the textfield to the answer, below is the code for that !
-(IBAction)calculate{
MyClass *setnum = [[MyClass alloc]init];
setnum.grade_num = grade;
setnum.stage_num = stage;
setnum.ex_lym = ex_ly;
setnum.pos_lym = pos_ly;
setnum.er_num = er;
setnum.noderatio = pos_ly/ex_ly;
if(text1.text.length <=0 ||text2.text.length <=0||text3.text.length<=0||text4.text.length<=0||text5.text.length <=0){
UIActionSheet *action = [[UIActionSheet alloc]initWithTitle:#"Incomplete Values" delegate:self cancelButtonTitle:#"Ok" destructiveButtonTitle:nil otherButtonTitles:nil];
[action showInView:self.view];
[action release];
}else{
answer *ans =[[answer alloc]initWithNibName:nil bundle:nil];
[self presentModalViewController:ans animated:YES];
float i = calc_gain(setnum.grade_num, setnum.noderatio, setnum.stage_num, setnum.er_num);
NSString *result = [NSString stringWithFormat:#"%f",i];
ans.answer1.text = result;
ans.bar.hidden = NO;
[ans release];
}
[setnum release];
}
You should consider using one of the provided container view controllers (UITabBarController, UINavigationBarController or UISplitViewController on the iPad and so on).
The way you use presentModalViewController is most likely the wrong way. For one, calling presentModalViewController will retain your views. Keeping allocating new controllers and displaying their views via presentModalView is therefore increasing your memory footprint with each navigation step.
In general, a viewcontroller which shows another modal viewcontroller is also responsible for dismissing it again. The way to dismiss a modal view controller is therefore to let the presented controller inform its parent through delegation and ask the parent to dismiss (often on tapping a 'done' button).
I'm not even sure whether stacking modalViewControllers is a supported scenario, but at least didn't find anything stated otherwise in the documentation.
Asked here yesterday:
Switching views for iphone application - is this the right way?
I think another good way to go about this is to do this and add a univanigationcontroller:
[self.navigationController pushViewController:second animated:YES];
I have been figuring out this since yesterday but have not got that correct yet.
I have added the modalviewcontroller for my loading view controller on top of my tab bar controller and it works fine.
Added the code in app Delegate:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions: (NSDictionary *)launchOptions {
[navController.navigationBar setTintColor:[UIColor blackColor]];
[window addSubview:rootController.view];
[window makeKeyAndVisible];
LoadingViewController *lvc = [[LoadingViewController alloc] initWithNibName:#"LoadingView" bundle:nil];
// Delegate added here
lvc.loadingDelegate = self;
[rootController presentModalViewController:lvc animated:YES];
[self URL];
[lvc release];
return TRUE;
}
Now I do my parsing and when its done I call the following code in different view name XMLParsingView.m where the parsing got over.
- (void)handleLoadedApps
{
LoadingViewController *loading = [[[LoadingViewController alloc] init] autorelease];
//delegating to let the load view controller know to dimiss itself by defining disappear method in protocol
[loading.loadingDelegate disappear];
}
and in loading view controller I have method which calls dismissModalViewControlAnimated:
-(void)disappear{
[activity stopAnimating];
[activity removeFromSuperview];
[self removeFromSuperview];
[self dismissModalViewControllerAnimated:YES];
}
But for some reason it will never remove the view and not load it back to my tab bar controller.
Really need help here if any one have come across such issues.
Sagos
In your code you seem to create, without a nib, a new LoadingViewController and immediately go and dismiss it. In your app delegate you create your first loadingViewController with a nib, present it modally on rootController and then release it. Since you want to dismiss it outside your app delegate you have
3 choices, (hardest to fastest and most sane)
a) Key-Value-Observing on a property of XMLParsingView from LoadingViewController to remove itself when the task finishes.
b) Use delegation to inform the LoadingViewController when the task finishes to dismiss itself.
c) Fetch your rootController from your [[UIApplication sharedApplication] delegate], which means you must expose rootController as a property or through a method, and make rootController dismiss your modal.
You need to call dismissModalViewControllerAnimated on the rootViewController, not the loading view controller.
I have an issue, viewWillAppear method in the UIViewController which is added to the screen by presentModalViewController method does not "go into" viewWillAppear method. Only time this method is invoked is together with the viewDidLoad, the first time.
- (IBAction)openModal:(id)sender {
if (self.nvc == nil) {
ModalViewController *vc = [[ModalViewController alloc] init];
self.nvc = vc;
[vc release];
}
self.nvc.segmentedIndex = [[self.navigationController.viewControllers objectAtIndex:0] index];
[self presentModalViewController:self.nvc animated:YES];
}
The property is of type retain.
Thanks in advance.
right, it works different and it now goes into viewWillAppear only once. So if you want to change the appearance of your view after dismissing a modal view you should do it using a modal delegate pattern. See the link:
http://useyourloaf.com/blog/2010/5/3/ipad-modal-view-controllers.html
Though it's for iPad, but you can get the idea. In the latest iOS versions it works the same.
Hope it helps