Changing navigationController.delegate causes to bad access - objective-c

This may sound a newbie question, however I'm new t iOS dev.
I've a view pushed in navigationController, let say it is the 3rd pushed view.
In that view I set self.navigationController.delegate = self;. I've changed delegate because I need to handle case when user goes to previous view i.e. pops from current view.
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if ([[viewController class] isEqual:[MainViewController class]]) {
...
}
}
It works OK, but when I pop the current view and press navigation back button again (i.e. switching to first pushed view) I'm getting bad access error.
So what I'm missing ?
What is the correct way to handle navigation back button press ?

It's because navigation controller sends a message to popped and deallocated view controller, you have to set the delegate each time you do the popping and pushing. Also add self.navigationController.delegate = nil; to dealloc method of your viewController.

Place below in the Viewcontroller in which you assigning self.navigationController.delegate = self
-(void) viewWillDisappear:(BOOL) animated
{
[super viewWillDisappear:animated];
if ([self isMovingFromParentViewController])
{
if (self.navigationController.delegate == self)
{
self.navigationController.delegate = nil;
}
}
}

Related

viewDidAppear not firing

I know this has been asked but I've tried all the solutions to no avail.
One of the tabs of my application is Logs
When it is selected a log viewcontroller with a list of logs is displayed
I have a "+" on the top menu bar to add new logs
When I select it I push the add log view controller on the stack
I add a new log - I then take the back button
when I return to the table the list is not updated
I wanted to reload the data in the table upon returning to the first view controller but
neither viewDidLoad, viewDidAppear or viewWillAppear are fired upon returning to the table
Since the first screen is oblivious
I added these to the second screen in an attempt to find any action occurring when the back button
-(void)viewWillDisappear{
NSLog(#" whered it go");
}
-(void)viewDidDisAppear
{
NSLog(#" disapeeeeeearing");
}
- (void)backAction {
NSLog(#" WHAT ABOTU THIS ");
[self.navigationController popViewControllerAnimated:YES];
}
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
[viewController viewWillAppear:animated];
NSLog(#"is ANY of this Happening");
}
NONE of these fire when the add screen disappears no NSLog printed
From what I've read the lack of response on the first screen is because the log table never really left the stack .
This didn't happen when I did this on another tab (different part of the app)
Whats different about this tab is the add log view controller has 5 subviews and can call modals (though it fails even when I don't call them) - I'm guessing it has something to do with that.
The call from the logs to the add logs
AddViewController *controller =[[AddViewController alloc] initWithNibName:#"AddViewController" bundle:nil];
controller.passString= newText;
[self.navigationController pushViewController:controller animated:YES];
Don't have any code for the return it just clicking on the back button at the top of the screen.
sorry for any misspellings
That's because you aren't using the correct methods.
-(void)viewWillDisappear{
NSLog(#" whered it go");
}
and
-(void)viewDidDisAppear
{
NSLog(#" disapeeeeeearing");
}
Aren't declared anywhere. I believe what you meant to write was:
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
NSLog(#"About to disappear");
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
NSLog(#"Did disappear");
}
Methods are case sensitive, and you can't just leave out arguments and expect it to work.

How to handle users pressing back button too quickly after pushing a viewController?

Usually after the viewController is pushed, I want to do certain things. In add phone feature, for example, I would open edit business and set the focus to the phone field.
If after viewController is pushed users press back button too quickly the app crash.
What's the standard way to do so?
This is the code:
+(BGBusinessEditViewController *) pushNewEditViewControllerWithBizandReturnValue: (Business *)biz withNavController :(UINavigationController *) nav andSelectPhone:(BOOL) selectPhoneAfterward
{
BGBusinessEditViewController * editBusiness = [[BGBusinessEditViewController alloc]init];
//[editBusiness view];//load the stuff first
[nav vPushViewController:editBusiness animated:YES andPerformBlock:^{
if (biz) {
editBusiness.biz=biz; //viewDidload must be called first before setting bizs
}
if (selectPhoneAfterward)
{
[editBusiness selectPhone];
}
}];
return editBusiness;
}
-(void) selectPhone
{
NSIndexPath * ipth =[NSIndexPath indexPathForItem:BGBusinessEditTextPhoneNumber inSection:0];
[self.tableView selectRowAtIndexPath: ipth animated:NO scrollPosition:UITableViewScrollPositionTop];
[self tableView:self.tableView didSelectRowAtIndexPath:ipth];
}
Basically I created a category in nav View Controller that will run code only when the navigation controller already reach - (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;
Well, Hide the back button until viewDidAppear, and then unhide it.

Cannot dismiss the Search view

I have a parent class with tableview and searchbar over it which is a subclass of tableview controller. Delegates for the searchBar and searchdisplaycontroller are set in a seperate class inherited from UISearchdisplaycontroller. The datasource and delegates for tableview and searchbar are handled in this class seperately. The classes are under ARC.
Hence, When a user taps on search, the control transfers from FilesListController (parent)class to this class. Now, When a user taps on cancel button, the searchbar delegate set in this class i.e.
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar
is CALLED but DOESN'T serve the purpose of dismissing the full screen searchtableview and return to the parentviewcontroller. However, if I don't write this delegate in the search class, it works properly. I have set the searchbar delegates in xib and on calling:
- (BOOL)searchBarShouldBeginEditing:(UISearchBar *)searchBar
like this:
self.searchResultsTableView.delegate = self;
self.searchResultsTableView.dataSource = self;
[parentFileViewController.searchDisplayController setDelegate:self];
Where am I going wrong? Thanks in advance.
If you want to dismiss a UISearchBar with a SearchBarController, just use this Code:
[self.searchDisplayController setActive:NO animated:YES];
you should implement resign the responder in the delegate function i.e
- (void)searchBarCancelButtonClicked:(UISearchBar *) searchBar {
[searchBar resignFirstResponder];
}
Memory warnings can appear at any time during the application run time, you must assume a memory warning will happen and the view and disposable objects will have to be recreated.
We are handling such situation by setting to nil our arrays:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
if([self isViewLoaded] && self.view.window == nil)
{
self.view = nil;
keys = nil;
names = nil;
errorDuringNetworkCall = nil;
}
}
And by dismissing the search bar tableview before performing the segue operation:
[self performSegueWithIdentifier:#"navigateToNextScreen" sender:self];
self.searchBar.text = #"";
[self.searchDisplayController setActive:NO animated:YES];
After a Memory warning is received the viewDidLoad method is called again and the arrays are populated, the search bar will continue to be useful.work without issues

Storyboard Segue not loading view

I'm trying to segue in code. the prepareForSegue function runs and shows correct destination view controller, but nothing happens. Any idéas on what I'm missing or what I have misconfigured?
View controller possible path:
A ->B (sgueResultat)
or
A->C (sgueStopSplash)
Code that chooses the segue:
if (proversion)
[self performSegueWithIdentifier:#"sgueResultat" sender:self];
}
else {
[self performSegueWithIdentifier:#"sgueStopSplash" sender:self];
}
`
The prepareForSegue that I can confirm is running by NSLog:
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSLog(#"Source Controller = %#", [segue sourceViewController]);
NSLog(#"Destination Controller = %#", [segue destinationViewController]);
NSLog(#"Segue Identifier = %#", [segue identifier]);
Nslog output:
2012-06-26 22:22:26.002 ClosetV2[2333:fb03] Source Controller = <ABStartViewController: 0xeb12270>
2012-06-26 22:22:26.002 ClosetV2[2333:fb03] Destination Controller = <SplashAfterStopViewController: 0x6b2c9e0>
2012-06-26 22:22:26.003 ClosetV2[2333:fb03] Segue Identifier = sgueStopSplash
2012-06-26 22:22:26.003 ClosetV2[2333:fb03] sgueStopSplash
Regards
Andreas
Make sure your segues are set to "modal" in your storyboard (or custom if you are fancy).
I remember when I first started using segues when storyboarding came out, I kept trying to use push, which you can only do if you have an embedded UINavigationController. Otherwise, a push segue will simply not function.
Another thought... where is the code below executing? If an animation is already taking place or if the current view is not already showing on the screen, it will not function properly. For example, if you have this code in your viewDidLoad, it will not work.
if (proversion)
[self performSegueWithIdentifier:#"sgueResultat" sender:self];
}
else {
[self performSegueWithIdentifier:#"sgueStopSplash" sender:self];
}
Edit: Updated Answer
Waiting for the [self dismissModalViewController:YES] animation to complete before performing your segue is actually annoying and somewhat messy. I don't think there is a "proper" way to do it. However, if you put the following method inside the view that has the modal controller and send it a message after using [self dismissModalViewController:YES], you should be in good shape.
- (void)waitUntilModalControllerIsDismissed
{
if(self.modalViewController)
{
[self performSelector:#selector(waitUntilModalControllerIsDismissed:)
withObject:nil
afterDelay:0.1f];
return;
}
else
{
//At this point, the modal animation will be completed and you can perform your segue if you want
}
}

Method should be called when a view gets into focus

in my iOS project I use InAppSettings. This is missing a delegate in the modal view controller for willDismiss.
So when the modal view gets dismissed I want a method to be called in my main view controller. How can I do this? Is there a method in a view controller that gets triggered whe the view is in focus again?
You could try something like this
BOOL settingsLaunched = NO;
-(void)presentInAppSettingsViewController
{
//Show the settings modal view controller here
//Set our flag
settingsLaunched = YES;
}
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if(settingsLaunched)
{
//Your code here
}
}
these will get called on the view after dismissing a modal dialog it presents
- (void) viewWillAppear
- (void) viewDidAppear:(BOOL)animated