Going back to mapview after having opened a newcontroller from an annotation disclosure button - viewcontroller

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];
}

Related

Swapping views inside a tabview controller

I have an app based on a tabBar controller. Within a certain view I'd like to add swipe gesture recognition and swap the current view with another one (which is not part of the tabBarController array). I have tried:
- (IBAction)swipeLeftDetected:(UIGestureRecognizer *)sender
{
//Does not work
UIViewController *DesiredViewController =[[UIViewController alloc] initWithNibName:#"DesiredViewController" bundle:nil];
DesiredViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:DesiredViewController animated:YES];
[self.view addSubview:DesiredViewController.view];
}
but the program crashes. The error I get is related to a SegmentedControl which is present in the next view but is absent in the current one. The views independently work perfectly!
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:'[<UIViewController 0xa355fb0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key X_SegmentedControl.'
I don't understand what I am doing wrong.. I want to swap the views completely rather than putting one on top of each other. Any advice please? Thanks
instead use this
-(IBAction)swipeLeftDetected:(UIGestureRecognizer *)sender
{
DesiredViewController *objView =[[DesiredViewController alloc]initWithNibName:#"DesiredViewController" bundle:nil];
objView.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentModalViewController:objView animated:YES];
[objView release]; // use release if using Non-ARC
}
The problem is not about the transition.
You are loading a view controller from a NIB.
A view controller is usually subclassed, so (assuming that your nib is configured correctly and you have .h and .m implementation file for your DesiredViewController subclass) you should init like this:
DesiredViewController *controllerInstance =[[DesiredViewController alloc] initWithNibName:#"DesiredViewController" bundle:nil];
The exception is because you probably have a segmented control inside your view controller subclass, Xcode is trying to link this control to the outlet on the view controller, but this outlet doesn't exists (because you are allocating an UIViewController not the subclass).

Pop to root then perform segue on iPhone

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];
}
}

How to navigate from Appdelegate to another viewcontroller

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];

iPhone SDK: Switching to one view then back to previous view errors

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.

ipad - dismissing a UIPopoverController

I have a button inside the content of a UIPopoverController. This button runs a method called myAction.
MyAction has the form
- (void) myAction:(id)sender
so, myAction receives the id of the caller button.
Now, inside this method I would like to dismiss the UIPopoverController, but the only thing I have is the ID of the caller button. Remember that the button is inside the UIPopoverController.
Is there a way to discover the ID of the UIPopoverController, given the button ID I already have?
thanks.
Unfortunately no. At least, not within the standard practices. You might be able to travel up the responder stack to find it, but it's a hack, it's buggy, and it's really, really messy.
If you want to dismiss a popover by pushing a button, some place relevant should keep a reference to the popover. Usually that would be the owner of the popover (not the controller showed within the popover). When the button is pressed, it can send a message to the owner controller, which can then dismiss the popover.
You might be tempted to have the controller displayed inside of the popover be the owner of its own popover, but coding this way is brittle, can get messy (again), and may result in retain loops so that neither ever gets released.
You can access the presenting popoverController by accessing "popoverController" with KVC.
[[self valueForKey:#"popoverController"] dismissPopoverAnimated:YES]
I have this working, and I do not think it is a hack. I have a standard split view iPad app. I then added a method on my detail controller (the owner of the pop over) to handle the dismissal.
On the standard split view architechture, both the root and detail view controllers are available via the app delegate. So I bound a button click inside the pop over to call a method which gets the app delegate. From there I call the method on the detail controller to dismiss the pop over.
This is the code for the method on the View Controller that is displayed inside the popover:
- (void) exitView: (id)sender {
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.detailViewController exitDrill];
}
Then the simple method to dismiss on the Detail View Controller:
- (void) exitDrill {
if(dtController != nil){
[dtController dismissPopoverAnimated: YES];
[dtController release];
}
}
I like the ability to do this because it give me a way to show a user how they can exit a pop over. This may not be necessary in future versions of the app; for right now, while this paradigm is still new to the platform, I prefer to let the users gexit a display in a couple fo different ways to make sure I minimize frustration.
As Ed Marty already wrote
If you want to dismiss a popover by pushing a button, some place relevant should keep a reference to the popover
This is very true; however, when showing a UIPopoverController, the class opening the popovercontroller keeps this resource already. So, what you could do is to use this class as the delegate class for your Popover Controller.
To do so, you could do the following, which I use in my code.
In the class opening the popover, this is my code:
- (void)showInformationForView:(Booking*)booking frame:(CGRect)rect
{
BookingDetailsViewController *bookingView = [[BookingDetailsViewController alloc] initWithStyle:UITableViewStyleGrouped booking:booking];
[bookingView setDelegate:self];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:bookingView];
self.popController = [[UIPopoverController alloc] initWithContentViewController:navController];
[self.popController setDelegate:self];
[self.popController setPopoverContentSize:CGSizeMake(320, 320)];
rect.size.width = 0;
[self.popController presentPopoverFromRect:rect inView:self.view permittedArrowDirections:UIPopoverArrowDirectionLeft animated:YES];
}
- (void)dismissPopoverAnimated:(BOOL)animated
{
[self.popController dismissPopoverAnimated:animated];
}
So what I am doing here is creating a UINavigationController and setting a BookingDetailsViewController as its rootViewController. Then I am also adding the current class as delegate to this BookingDetailsViewController.
The second thing I added is a dismissal method called dismissPopoverAnimated:animated.
In my BookingDetailsViewController.h I added the following code:
[...]
#property (nonatomic, strong) id delegate;
[...]
And in my BookingDetailsViewController.m I added this code:
[...]
#synthesize delegate = _delegate;
- (void)viewDidLoad
{
UIBarButtonItem *closeButton = [[UIBarButtonItem alloc] initWithTitle:#"Close" style:UIBarButtonItemStylePlain target:self action:#selector(closeView)];
[self.navigationItem setRightBarButtonItem:closeButton];
[super viewDidLoad];
}
- (void)closeView
{
if ([self.delegate respondsToSelector:#selector(dismissPopoverAnimated:)]) {
[self.delegate dismissPopoverAnimated:YES];
}
else {
NSLog(#"Cannot close the view, nu such dismiss method");
}
}
[...]
What happens is that when the "Close" button in the UINavigationController is pressed, the method closeView is called. This method check if the delegate responds to dismissPopoverAnimated:animated and if so, it calls it. If it does not respond to this method it will show a log message and do nothing more (so it wont crash).
I have written my code using ARC, hence there is no memory management.
I hope this helped you.