I'm doing an App with a MKMapView that has a back button the UIView which contain the MKMapView in order to go to main menu, that's ok, but when I want to load again the UIView with the MKMapView my App crash, it doesn't gives any error, just crash and show machine code where it crashed, but it says first: com.apple.CoreLocation.ConnectionClient.0x1e5d5220 and then lot of machine code.
The crash report is here:
I have to add, that the first time I load that UIView it's 100% working.
Thanks for your help.
PS: Why I say dealloc? because I think doing something like dealloc probabbly will fix my problem and will be the same as running the first time.
EDIT1:
My - (void) viewDidLoad; method.
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *response = [self sendPostToURL: #"http://hidden.php"
withPost: #"hidden"];
[self tratarXML: response];
yo = #"Posición actual";
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
self.locationManager.distanceFilter = kCLDistanceFilterNone;
[self.mapView setDelegate: self];
[self.mapView setZoomEnabled: NO];
[self.mapView setScrollEnabled: NO];
if ([CLLocationManager locationServicesEnabled])
{
self.locationManager.delegate = self;
self.mapView.showsUserLocation = YES;
[self.mapView setMapType: MKMapTypeStandard];
[self.mapView setUserTrackingMode: MKUserTrackingModeFollowWithHeading animated: NO];
[self.locationManager startUpdatingLocation];
//[self.locationManager startUpdatingHeading];
self.mapView.userLocation.title = yo;
}
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"Debe activar la localización en esta aplicación para funcionar"
delegate:self
cancelButtonTitle:#"Aceptar"
otherButtonTitles:nil, nil];
[alert show];
}
}
EDIT2:
My back button code:
- (IBAction) iniciar: (id)sender
{
if ([iniciar.title isEqualToString:#"Volver"])
{
menuViewController *obj = [[menuViewController alloc] initWithNibName:#"menuViewController" bundle:nil withUser:user];
[self presentViewController:obj animated:YES completion:nil];
}
else
{
// censored
}
I'm using simple *.xib navigator, not storyboard, etc...
EDIT3:
The menu button that load the MKMapView UIView
- (IBAction) TEST: (id)sender
{
mapaViewController *obj = [[mapaViewController alloc] initWithNibName: #"mapaViewController" bundle: nil withUser: user];
[self presentViewController:obj animated:YES completion:nil];
}
There are 3 things that are wrong:
Your back button code should not call a presentViewController but a dismissViewController. Right now you are adding view controller after view controller! When you go back you want to instead get rid of your view controller.
In the back button code, add self.locationManager.delegate = nil;
Similarly, in the back button code, add self.mapView.delegate = nil;
That should do it.
It looks like you're doing a series of presentViewController calls to go from main menu to the map view controller, and then back to the main menu. This is not how it should be done. You probably shouldn't be doing modal transitions at all, but if you do, you should have the main menu dismiss the map view controller, not have the map view controller present another main menu controller.
The better way to do this would be with a navigation controller. Main menu should be the root view controller of a navigation controller, and you would push to go to the map view controller. You will automatically get a back button in the navigation bar, which will take you back (to the same instance) of the main menu controller. No need for any back button code. All this would be much easier to implement in a storyboard.
Related
I am developing an iOS app which contains login/authentication functionality - basically first time a user logins, in they need to enter relevant login details - then they are passed to main app screens - subsequent visits to the app they will be automatically authenticated.
All above works fine - the issue I have is with the Navigation bar - it appears in the main screen in the main part of the app with a back button - I don't want this to be displayed as they should not be able to return to the login screen once authenticated. I guess it's using the root navigation controller which explains the logic, but is there a way to ignore the navigation controller of the login section so the back button is not displayed in the main app.
Below is a screenshot of the structure to help my explanation - left hand group of screens are the login process right hand is the main app structure.
The code used to switch screens is as follows -
SWRevealViewController *swRevealController = (SWRevealViewController *)navVC;
swRevealController.managedObjectContext = self.managedObjectContext;
[self.navigationController pushViewController:controller animated:YES];
Don't push view controller. Create new hierarchy with:
Objective-C
[self.navigationController setViewControllers:#[controller] animated:YES];
Swift
navigationController.setViewControllers([controller], animated:true)
In the Screen which implements after login, the ViewDidLoad method add the line to hide back bar button.
self.navigationItem.hidesBackButton = YES;
Additionally you can add an 'Logout' option as
UIBarButtonItem *backBarButton = [[UIBarButtonItem alloc] initWithTitle:#"Logout" style:UIBarButtonItemStyleBordered
target:self
action:#selector(LogoutClick)];
self.navigationItem.leftBarButtonItem = backBarButton;
-(void)LogoutClick {
[self showLogoutAlert];
}
-(void)showLogoutAlert {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#""
message:#"Do you want to Logout ?"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Logout", nil];
[alert show];
}
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) {
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
Use a modal view controller for the login screen so its not part of your navigation controller hierarchy within your navigation root probably in view did load - sample code below:
- (void) viewDidLoad {
......
LoginVC *loginViewController = [LoginVC alloc] init];
loginViewController.parent = self;
[self presentViewController: loginViewController animated:YES completion:NULL];
return;
}
You may or may not dismiss the modal view from the root - if you do, you will need to be able to call the dismiss routine from loginViewController but there are other ways as well and you can put the dismiss inside the modal view (loginViewController)
(void) login_exit:(BOOL)success {
[self dismissViewControllerAnimated:YES completion:NULL];
if !(success) {
.... send message (UIAlertview and ask if he or she wants to try again)
}
else {
}
return;}
Very simple.. Just navigate to rootviewcontroller
SWRevealViewController *swRevealController = (SWRevealViewController *)navVC;
swRevealController.managedObjectContext = self.managedObjectContext;
//-- I think above lines not needed as per your question
[self.navigationController popToRootViewControllerAnimated:YES];
You need to hide the Navigation Bar in the ViewController before you push the SWRevealViewController by using the below line in viewDidLoad
Objective C
self.navigationController.navigationBarHidden = true;
Swift
self.navigationController?.navigationBarHidden = true;
It will not show the back button in the next view controller pushed
2022 answer
#IBAction func SomeScreen() {
let s = UIStoryboard ...
navigationController?.isNavigationBarHidden = true;
navigationController?.pushViewController(s, animated: true)
}
It's that easy.
Write this in viewDidLoad method
self.navigationItem.hidesBackButton = YES;
This will hide the back button for the first view.
ClassA* controller = [storyboard instantiateViewControllerWithIdentifier:#"ClassAIdentifier"];
if (controller != nil) {
[self.navigationController pushViewController:controller animated:YES];
[controller.navigationItem setHidesBackButton:YES animated:NO];
}
You MUST hide the navigationItem after push, otherwise it will be nil.
Is is possible to have a UIView preloaded so that it will load faster when the user taps on button to load it? Currently I've got a library of informaiton that I'm attempting to load when the user taps a button, and for now it seems to be "ok" , but it makes the navigation to the page choppy, because of all the information in the library it's loading.
Thanks in advance!
It should be possible to split the setup of a View Controller from code that displays the view after a button is pushed. This will eliminate the lag when the button but the task to setup the view controller still need to be done sometime during execution (You can for example put it in the ViewdidAppear method so it is executed while waiting for the button to be pushed.
Take this code for example:
-(IBAction) button_pushed {
/*setup */
NewVC *vc = [[NewVC alloc] init];
vc.var1 = var1;
vc.var2 = x;
[vc setup];
/*display */
[self.navigationController pushViewController:vc animated:NO];
return;
}
You can split the code that setup the view from the code that displays the view
into :
#synthesize vc;
…..
- (NewVC) setup {
//setup
NewVC *vc1 = [[NewVC alloc] init];
vc.var1 = var1;
vc.var2 = x;
[vc setup];
return(vc1);
}
-(void) ViewDidAppear {
if (setupready) {
vc = [self setup];}
return;
}
-(IBAction) button_pushed:(ID) sender {
//display
[self.navigationController pushViewController:vc animated:NO];
return;
}
I have two programmatically created buttons you can see in my viewDidLoad method. On the modal window I have a button that calls the cancelSearch method via a delegate. When I place a breakpoint on my cancelSearch method it is hit, so I know my delegate is set up correct, but even though it calls this line [self dismissViewControllerAnimated:YES completion:nil]; it's not actually closing the modal window.
The code below is all from my main controller view.
- (void)viewDidLoad
{
[super viewDidLoad];
UIBarButtonItem *actionButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAction target:self action:#selector(showActionMenu:)];
actionButton.style = UIBarButtonItemStyleBordered;
UIBarButtonItem *searchButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSearch target:self action:#selector(showSearchMenu:)];
searchButtonItem.style = UIBarButtonItemStyleBordered;
UIToolbar* toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 103.0f, 44.01f)];
NSArray* buttons = [NSArray arrayWithObjects:actionButton, searchButtonItem, nil];
[toolbar setItems:buttons animated:NO];
self.navigationItem.title = #"Census Management";
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:toolbar];
[[RKClient sharedClient] get:#"censusmanagement" delegate:self];
}
- (IBAction)showActionMenu:(id)sender
{
[self performSegueWithIdentifier: #"CMActionSegue" sender: self];
}
- (IBAction)showSearchMenu:(id)sender
{
ehrxCMSearchView *search = [[self storyboard] instantiateViewControllerWithIdentifier:#"cmSearch"];
search.selectedOptions = self.selectedOptions;
search.delegate = self;
[self.navigationController pushViewController:search animated:YES];
}
- (void)cancelSearch:(ehrxCMSearchView *)controller
{
[self dismissViewControllerAnimated:YES completion:nil];
}
You would dismiss a modal view using something similar to:
[self dismissModalViewControllerAnimated:YES];
This will dismiss the modal view which was loaded using something similar to:
[self presentModalViewController:search animated:YES];
However looking at your code snippet, it appears the search view controller is being pushed onto the navigation stack using the following line:
[self.navigationController pushViewController:search animated:YES];
So I you probably need to pop the view from the navigation stack rather than trying to dismiss it as a modal view:
[self.navigationController popViewControllerAnimated:YES];
If your view controller is modally presented, you should use this:
[self.presentingViewController dismissModalViewControllerAnimated:YES];
The presentingViewController property is available in iOS 5 only. So, if you're targeting older versions of iOS, you have to use self.parentViewController instead (use the appropriate one for each iOS version, you have to handle this).
If you make this control in your parent/presenting view controller, then just call this:
[self dismissModalViewControllerAnimated:YES];
Update at bottom of question
Here's what I'm looking to do:
Main ViewController opens a new ViewController (i don't want a navigation bar, so do I have to do this modaly?) which then opens the camera.
In the main view controller I have this being called on a button press:
CameraProcessViewController *controller = [[CameraProcessViewController alloc] initWithNibName:#"CameraProcessViewController" bundle:nil];
[self presentModalViewController:controller animated:NO];
Which opens up the next controller. In that next controller in the viewDidLoad method I have:
if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];
[imagePicker setSourceType:UIImagePickerControllerSourceTypeCamera];
[imagePicker setDelegate: self];
[imagePicker setAllowsEditing:NO];
[self presentModalViewController:imagePicker animated:YES];
NSLog(#"Has camera");
} else {
NSLog(#"Has no camera");
}
I've tried both presentModalViewController and presentViewController and every combination of the two between the main view controller and this child one, however the camera never opens. When I move the camera code into the main view controller it opens fine.
I've looked and looked and I can't find anything saying why this would be.
Thanks!
UPDATE
I found that in the viewDidLoad method I can call this:
[self performSelector:#selector(openCamera) withObject:nil afterDelay:0];
And it loads it up just fine. Is this okay or is this not recommended? Seems a tad hacky!
viewDidLoad is not the right place to do this. You could try viewWillAppear but that might be too early, too. Otherwise, viewDidAppear.
Sams **Teach Yourself iPad Application development in 24 hour's says I can "display an action sheet in a "nonanimated" fashion, filling a full popover view when it first appears...To do this, you need to show the action sheet with the method
showFromRect:inView:animated
with the "rect" set to the dimensions of the popover, the view set to the popover view controller's view, and "animated" set to false. The display of the action sheet would need to take place when the popover view is first loaded such as in the viewDidLoad method of the popover view controller.
OK, easy.. here's my code in my popover's viewDidLoad method:
- (void)viewDidLoad {
self.contentSizeForViewInPopover=CGSizeMake(400.0,400.0);
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Available Actions" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"Destroy" otherButtonTitles:#"Negotiate", #"Compromise", nil];
[actionSheet showFromRect:[self.view bounds] inView:self.view animated:NO];
[actionSheet release];
[super viewDidLoad];
}
But this fails every time at the inView:self.view parameter with the exception:
Invalid parameter not satisfying view != nil
Any ideas?
Note, if I put this exact same code in an IBAction method and trigger it from a button in the popover, it works without a hitch!
One solution is to call the UIActionSheet in viewWillAppear or viewDidAppear: For example:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self showActionSheet];
}
- (void)showActionSheet {
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:#"Available Actions" delegate:self cancelButtonTitle:#"Cancel" destructiveButtonTitle:#"Destroy" otherButtonTitles:#"Negotiate", #"Compromise", nil];
[actionSheet showFromRect:[self.view bounds] inView:self.view animated:NO];
[actionSheet release];
}
self.view hasn't been instantiated completely yet when this code is called.
I would suggest, as a hacky alternative, to put in a short (.1 seconds or something) NSTimer with your IBAction method as the callback.