In my iPhone app, I am using Pushnotifications. When I receive a notification, I want to move to another viewcontroller from the current viewcontroller. I wrote the - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex in the appdelegate itself. And I am using View based application, so I want to use presentModalViewController (i think). And I used following code:
UIViewController *view = (UIViewController*)self.viewController.presentedViewController;
[view presentModalViewController:self.anotherVC animated:NO];
but got error
2012-06-19 17:47:52.521 iPhoneApp[1450:607] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present a nil modal view controller on target <HomeScreenVC: 0x1fb77cc0>.'
what went wrong here? Any idea.
Edit
I will elaborate the issue:
I have viewcontrollers VC1, VC2, VC3 and VC4. My rootviewcontroller is VC1. If a push notification come when I am at VC2 or VC4, when I click on Ok button of notification alert, I want to move to VC3. But the delegate method for the alert view is in appdelegate.m.
I have updated my code like
self.menuVC = [[MenuScreenVC alloc] initWithNibName:#"MenuScreenVC" bundle:nil];
UIViewController *view = (UIViewController*)self.viewController.presentedViewController;
[view presentModalViewController:self.menuVC animated:NO];
now I can navigate to VC3 if i am at VC2, I can't if i am at VC4. Please help
Instead of what you used do the following
[self.window.rootViewController presentModalViewController:self.anotherVC animated:NO];
Related
I am making an application where there are multiple pins in a map. (using xcode for iOS apps) When I click on the pins, the callout comes up with a disclosure button that when pressed brings up a new viewcontroller (which I use as the detail view.. is this correct?)
I am currently having problems going back to the original viewcontroller after viewing the new viewcontroller.
How should I proceed to go back to the map view?
I have tried with the -(IBAction)Back; command and linking it to a button on the new viewcontroller, however when I click it in the simulator, a black screen comes up and no error is shown in the output..
Any help would be appreciated!
I used the following to view the new view controller:
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {
if ([view.annotation.title isEqualToString:#"..."]) {
...Controller *sampleView = [[...Controller alloc] init];
[self presentModalViewController:sampleView animated:YES];
}
if ([view.annotation.title isEqualToString:#"..."]){
...ViewController *sampleView = [[...ViewController alloc] init];
[self presentModalViewController:sampleView animated:YES];
}
}
EDIT 1:
This is the error code I get after making the change..
2013-06-30 18:02:30.386 lam[15156:13d03] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "...Controller" nib but the view outlet was not set.'
*** First throw call stack:
(0x1e2d012 0x126ae7e 0x1e2cdeb 0xf88c8 0xf8dc8 0xf8ff8 0xf9232 0x104c25 0x3043a3 0x101ee3 0x102167 0xfee0071 0x374c 0x10deb1a 0x10ea28e 0x82f617f 0x127e705 0x1b2c0 0x1b258 0xdc021 0xdc57f 0xdb6e8 0x4acef 0x4af02 0x28d4a 0x1a698 0x1d88df9 0x1db0f3f 0x1db096f 0x1dd3734 0x1dd2f44 0x1dd2e1b 0x1d877e3 0x1d87668 0x17ffc 0x2842 0x2775)
libc++abi.dylib: terminate called throwing an exception
How did your calloutAccessoryControlTapped "bring up" this new view controller?
If you used a modal transition (e.g. such as presentViewController or modal segue), then you would dismiss it with dismissViewControllerAnimated. If you used the deprecated presentModalViewController, then you'd use the dismissModalViewControllerAnimated, but then again, you probably shouldn't use the deprecated methods unless you need to support iOS versions prior to 5.0. Use the presentViewController and dismissViewControllerAnimated renditions, instead. Anyway, you might end up with an IBAction method like:
- (IBAction)handleDoneButton:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
If you used push transition (e.g. pushViewController or push segue), then you'd generally just avail yourself of the built-in "back" button, but if you need your own button to pop this controller, you'd use an IBAction with popViewControllerAnimated. For example:
- (IBAction)handleDoneButton:(id)sender
{
[self.navigationController popViewControllerAnimated:YES completion:nil];
}
Or, if your calloutAccessoryControlTapped used a performSegueWithIdentifier, then you could either use the above technique or, in iOS 6 and above, you could define the following unwind segue in the view controller that has the map:
- (IBAction)backToMap:(UIStoryboardSegue *)segue
{
// do whatever you want
}
And then you can control-drag from the button to the exit outlet in the bar under the scene, and you should see this backToMap unwind segue.
Regarding your error, it means that your NIB's root view is not set. Select the view and look at its outlets. You should see something like:
If not, (a) make sure you set your "File's owner" to be your view controller class; and (b) right click on "File's owner"; (c) drag from the "o" next to the "view" in the popover view to the actual view:
Also, when creating the view controller, you'd probably want to specify which NIB to use:
ViewController *controller;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
controller = [[ViewController alloc] initWithNibName:#"ViewController_iPhone" bundle:nil];
} else {
controller = [[ViewController alloc] initWithNibName:#"ViewController_iPad" bundle:nil];
}
I am running on iOS 6.0 storyboard enabled
I have a NavController linked to a TableViewController.
This TableView can segue to AViewController or BViewController.
When I am in A, I want to pop back to the root and perform segue to B with this line :
UINavigationController *nav = self.navigationController;
[nav popToRootViewControllerAnimated:YES];
[nav performSegueWithIdentifier:#"GoToB" sender:self];
I checked the storyboard, GoToB do exist and is linked from the TableViewController to BViewController
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Receiver (<NavMainViewController: 0xb921fa0>) has no segue with identifier 'GoToB''
What am I missing ?
The segue will be attached to the view controller you pop to, not nav which is the container view controller that contains it. So this would be closer:
UINavigationController *nav = self.navigationController;
[nav popToRootViewControllerAnimated:YES];
UIViewController *rootVC = [nav.viewControllers objectAtIndex:0];
[rootVC performSegueWithIdentifier:#"GoToB" sender:self];
But, I think the problem here will be that the pop animation will conflict with the segue. Doing the pop with ...Animated:NO might fix it, but I think it would be more correct (and more robust for animations) to perform the segue from the rootVC.
rootVC would implement viewDidAppear as follows:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
if (!self.isBeingPresented && /* any other condition that makes you want this */) {
[self performSegueWithIdentifier:#"GoToB" sender:self];
}
}
My ViewController1 pushes ViewController2
ViewController2 *controller =
[[ViewController2 alloc] init];
[self.navigationController pushViewController:controller
animated:NO];
[controller release];
ViewController2 has UITableView ... in xib file I connected delegate with File's Owner. Also ViewController2 has Done button
- (IBAction)doneButtonPressed {
[self.navigationController popViewControllerAnimated:NO];
}
The problem is that if to click table rows and done button at the same time, sometimes didSelectRowAtIndexPath: method calls after that ViewController2 was popped, and I have SIGABRT error and this thing in logger :
[__NSCFSet tableView:didSelectRowAtIndexPath:]: unrecognized selector sent to instance 0x62579d0'
So how tableView:didSelectRowAtIndexPath can be called after I popped viewController2 ? It should be dead...
One easy fix is to do this:
- (IBAction)doneButtonPressed {
self.tableView.delegate = nil;
[self.navigationController popViewControllerAnimated:NO];
}
That way you guarantee that while you're leaving that view no more delegate calls will happen. You could also do this in the dealloc method for the view controller (probably a better place for it).
popViewController & pushViewController methods perform their tasks asynchronously. They use an animation block to slide the viewController in and out. The ViewController gets removed from its superview and gets released in the completion portion of the animation block. The crash is because of this delay (I think 0.3 seconds).
I am using the following code to push my view controller, except when the UIButton is pressed - nothing happens apart from the NSLog statement:
-(IBAction)doChangePasscode{
NSLog(#"Change Passcode Screen Loaded!");
ChangePasscode *cpscreen = [[ChangePasscode alloc] initWithNibName:#"ChangePasscode" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:cpscreen animated:YES];
}
I have imported the relavant files (using #import) so everything should be fine...
Why is this happening?
Thanks!
Have you debugged and checked that your navigationController isn't nil ?
You must check first from which button click action you are push viewcontroller That viewcontroller must be embed in UINavigationcontroller, after that you will able to push viewcontroller from that Navigation viewcontroller.
Your parent Viewcontroller must be embed in UINavigationviewcontroller.
I have a UITabBarConroller that I use to switch between 3 different views. This all works perfectly. On one of my tabs, I added a button at the to called "Add", I have added an outlet to this, as well as an IBAction method which looks like the following:
// Method used to load up view where we can add a new ride
- (IBAction)showAddNewRideView {
MyRidesViewController *controller = [[MyRidesViewController alloc] initWithNibName:#"AddNewRide" bundle:nil];
controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:controller animated:YES];
[controller release];
}//end showAddNewRideView
This currently works fine, and loads up my AddNewRide nib file. But, once that view loads, I have a cancel button, which, when clicked, I want to return to the previous view. So, I figured I would just do the reverse of the above, using the following method which would load back my previous nib:
- (IBAction)cancelAddingNewRide {
MyRidesViewController *controller = [[MyRidesViewController alloc] initWithNibName:#"MainWindow" bundle:nil];
controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:controller animated:YES];
[controller release];
}//end cancelAddingNewRide
But, which trying to load the MainWindow nib, the program crashes, and I get the following error:
2010-05-05 20:24:37.211 Ride[6032:207] *** -[MyRidesViewController cancelAddingNewRide]: unrecognized selector sent to instance 0x501e450
2010-05-05 20:24:37.213 Ride[6032:207] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[MyRidesViewController cancelAddingNewRide]: unrecognized selector sent to instance 0x501e450'
So, I am a little lost as to why it would work one way, but not the other.
First, I wanted to address part of the error: Think of your views as a stack. When you "push" a modal controller, you are adding that view to a stack. The old view is still there underneath. So you need to "pop" off the modal view to return to the old view. If you push a new view on, you now have 3 views on the stack which are all taking up memory, where you really only need one.
So, inside cancelAddingNewRide just try:
[super dismissModalViewControllerAnimated:true];
You may have other issues that are causing the crash, but this should generally get things working.
Typically when I have used presentModalViewController the presented viewController tells the calling viewController to dismiss it using dismissModalViewControllerAnimated:YES;
So in other words in the cacncelAddingNewRide you simply call the class that hass showAddnewRideView in it and have it pass itself to the method.
It's hard to explain but I'll show you an example:
cancelAddingNewRide class:
- (IBACtion)home:(id)sender {
if (self.delegate respondsToSelctor:#selector(dismiss:)]) {
[self.delegate dismiss:self];
}
}
and then in the showAddNewRideView class
-(void) dismiss:(cancelAddingNewRide_class *) controller {
[self dismissModalViewControllerAnimated:Yes];
}
Hope that makes sense and soz for typos
Edit: oh and make the controller's delegate self
controller.delegate = self;
Actually thinking about it more there is a bit more to this. You have to define the called viewController as a Delegate. Have a look at Stanford universities iPhone lectures, lecture 11 deals with this and is available from iTunesU.