I've got a custom navigation controller declared as below. My problem is that once I implement this, the back swipe gesture to go back to previous stack (interactivepopgesturerecognizer) is not working. How can I enable it back? I've got a lot of view controller in my app. Thank You.
#import "NavController.h"
#interface NavController ()
{
BOOL shouldIgnorePushingViewControllers;
}
#end
#implementation NavController
-(instancetype)init {
self = [super init];
self.delegate=self;
return self;
}
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (!shouldIgnorePushingViewControllers)
{
[super pushViewController:viewController animated:animated];
}
shouldIgnorePushingViewControllers = YES;
}
- (void)didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
shouldIgnorePushingViewControllers = NO;
}
#end
Try to enable property
self.interactivePopGestureRecognizer.enabled = YES;
to init method
Related
I have gone through most of the previous related posts, but although I have followed them correctly (as far as i understood), I simply am not able to trigger the delegate method for below code.
Objective: ModalView generates a string *SQL_String. Press DONE to dismiss the ModalView and trigger the delegate method in the parentview to get that *SQL_String.
SearchModalViewController.h
#protocol SearchControllerDelegate
- (void)didDismissModalView:(NSString *)SQL_String;
#end
#interface SearchModalViewController : UIViewController
#property (nonatomic, assign) id <SearchControllerDelegate> searchDelegate;
- (IBAction)handleDone:(id)sender;
SearchModalViewController.m
#interface SearchModalViewController ()
#end
#implementation SearchModalViewController
#synthesize searchDelegate;
- (IBAction)handleDone:(id)sender {
[self dismissView:sender];
}
- (void)dismissView:(id)sender {
[searchDelegate didDismissModalView:#"Test"];
[self dismissViewControllerAnimated:YES completion:nil];
}
DetailViewController.m (My parent View Controller)
#interface DetailViewController () <SearchControllerDelegate>
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
SearchModalViewController *searchModal = [[SearchModalViewController alloc] init];
searchModal.searchDelegate = self;
}
PROBLEM:
Below delegate method is not getting triggered.
- (void)didDismissModalView:(NSString *)SQL_String {
[self dismissViewControllerAnimated:YES completion:nil];
NSLog(#"The string = %#", SQL_String);
}
Any idea where I am doing wrong?
EDIT: Thank you guys. With your fast suggestions, I am able to close it down by adding below code instead of my previous IB connection.
- (IBAction)showSearchModal:(id)sender {
SearchModalViewController *searchModal = [self.storyboard instantiateViewControllerWithIdentifier:#"search"];
searchModal.searchDelegate = self;
searchModal.modalPresentationStyle = UIModalPresentationFormSheet;
searchModal.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self presentViewController:searchModal animated:YES completion:nil];
}
Here it goes
Change DetailViewController.m
- (IBAction)pushToSearch:(id)sender{
SearchModalViewController *searchModal = [self.storyboard instantiateViewControllerWithIdentifier:#"search"];
searchModal.searchDelegate = self;
[self presentViewController:searchModal animated:YES completion:nil];
}
And it will work.
Firstly, make sure your dismissView: of the SearchModalViewController is getting triggered.
Secondly, make sure your searchDelegate in the dismissView: method is not nil.
You need to set the delegate when you present the SearchModalViewController. The reason why your code doesn't currently work, is because the modal view controller's delegate is nil.
Update:
You set the delegate in prepareForSegue:sender:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)__unused sender
{
if ([[segue identifier] isEqualToString:#"modalSearch"])
{
SearchModalViewController *controller = (SearchModalViewController *)[segue destinationViewController];
controller.delegate = self;
}
}
I have a series of UIViewControllers in my app, and these are presented using a UINavigationController. The HOME ViewController calls pushViewController to get to the PROFILES ViewController. The PROFILES screen correctly shows the back button to HOME. Then selecting a button on PROFILES will take the user to the DETAIL ViewController. The DETAIL screen correctly shows the back button to PROFILES. But when I press the back button to PROFILES, I do indeed go back to the PROFILES screen but the navigation bar on that screen now shows the title HOME with no back button. In other words, it appears that IOS 8 has popped the ViewController once to go back from DETAIL to PROFILES, but has somehow popped the navigation bar items twice!
Any idea how I can fix this?
Apparently the solution outline can be found here:
UINavigationController and UINavigationBarDelegate.ShouldPopItem() with MonoTouch
In my solution, I just have all my view controllers extend CustomUINavigationController, which looks like this:
#import "CustomUINavigationController.h"
#import "IOSVersion.h"
#interface CustomUINavigationController ()
#end
#implementation CustomUINavigationController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.delegate=self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//
// The following two methods are the key to overriding the buggy behavior in IOS 8
// The first method is from here:
// https://stackoverflow.com/questions/6413595/uinavigationcontroller-and-uinavigationbardelegate-shouldpopitem-with-monotouc
//
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
NSLog(#"Inside shouldPopItem");
if (regularPop) {
NSLog(#"regularPop is TRUE");
} else {
NSLog(#"regularPop is FALSE");
}
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
if (regularPop) {
regularPop = FALSE;
return YES;
}
regularPop = TRUE;
[self popViewControllerAnimated:YES];
return NO;
} else {
return [super navigationBar:navigationBar shouldPopItem:item];
}
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
NSLog(#"Inside popViewControllerAnimated");
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:self.viewControllers];
int cnt=(int)[viewControllers count];
NSLog(#"Inside popViewControllerAnimated, cnt is %d",cnt);
UIViewController *vc=[viewControllers objectAtIndex:cnt-2];
if (regularPop) self.desiredVC=vc;
[self popToViewController:vc animated:animated];
return vc;
} else {
return [super popViewControllerAnimated:animated];
}
}
- (UIViewController *)manualpopViewControllerAnimated:(BOOL)animated {
NSLog(#"Inside manualpopViewControllerAnimated");
if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(#"8.0")) {
regularPop=TRUE;
return [self popViewControllerAnimated:animated];
} else {
return [super popViewControllerAnimated:animated];
}
}
- (void)navigationController:(UINavigationController *)navigationController
didShowViewController:(UIViewController *)viewController
animated:(BOOL)animated {
NSLog(#"Inside didShowViewController");
if (viewController==self.desiredVC) {
NSLog(#"Inside didShowViewController, found desiredVC");
regularPop = FALSE;
}
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
#end
I have an app which contains a UITabBarController. The last tab should only be entered when a condition is met. Currently I am checking this condition at the function viewDidAppear. But is it also possible to do this check every time before the tab is opened and displayed?
According to the first response I added this two files:
MainTabBarController.h
#import <UIKit/UIKit.h>
#interface MainTabBarController : UITabBarController
#end
MainTabBarController.m
#import "MainTabBarController.h"
#interface MainTabBarController ()
#end
#implementation MainTabBarController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
- (void)tabBarController:(UITabBarController *)theTabBarController didSelectViewController:(UIViewController *)viewController
{
NSUInteger indexOfTab = [theTabBarController.viewControllers indexOfObject:viewController];
if(indexOfTab == 2)
{
NSLog(#"Is it working?");
}
}
#end
In MainTabBarController.m file,add a delegate
#interface MainTabBarController ()<UITabBarControllerDelegate>
Then in viewDidLoad, add
self.delegate = self;
Then add a method:
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController: (UIViewController *)viewController
{
//add your check here
return YES;
}
is it also possible to do this check every time before the tab is
opened and displayed?
You need to override the UITabBarController delegate method for this :
- (void)tabBarController:(UITabBarController *)theTabBarController didSelectViewController:(UIViewController *)viewController {
NSUInteger indexOfTab = [theTabBarController.viewControllers indexOfObject:viewController];
if(indexofTab == yourTabIndexToMetCondition){
// Do your stuff here
}
}
This question already has an answer here:
Navigation bar overlapped by status bar
(1 answer)
Closed 9 years ago.
Note: This problem seems to occur after modal views are dismissed.
I am subclassing my UINavigationController to make it so that it can't be rotated when on a view that contains navigation. Here is what the code looks like... very simple. I just set my UINavigationController class in Interface Builder to NavViewController:
#import "NavViewController.h"
#interface NavViewController ()
#end
#implementation NavViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate
{
return NO;
}
#end
The problem is, this is causing my top bar position to get messed up when coming back from a modal view. For example, here is what it looks like when I navigate to the view (Just FYI: the black on the top status bar is built into the background image for the Nav bar):
Then on my view controller with the Nav bar on it, I call this code to get a picture from the photo album:
- (IBAction)goToPhotoAlbum
{
UIImagePickerController *mypicker = [[UIImagePickerController alloc] init];
mypicker.delegate = self;
mypicker.allowsEditing = NO;
mypicker.wantsFullScreenLayout = YES;
mypicker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:mypicker animated:YES completion:nil];
}
and then I dismiss it:
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
[self dismissViewControllerAnimated:YES completion:nil];
}
When the view reappears, the Nav bar now looks like this:
When I comment out the following code from my Nav subclass, like so, everything seems to work fine again and the bar is positioned correctly:
/*
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotate
{
return NO;
}
*/
Anyone know what is going on here and how I can maybe fix this?
Answering my own question:
This is actually a duplicate of Navigation bar overlapped by status bar
The fix was to change my code in the subclass to this:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
- (BOOL)shouldAutorotate
{
return YES;
}
I may have some misunderstanding regarding the use of the UINavigationControllerDelegate protocol. Here is my situation:
I have a ViewController, let's call it, BViewController that may display a PopoverViewController. BViewController is the second ViewController in a NavigationContoller's stack, after AViewController. I need to dismiss the PopoverViewController when the user hits a button in BViewController and the app takes us back to the previous view--AViewController.
To do that, I have implemented the following in BViewController
- (void)viewWillDisappear:(BOOL)animated {
NSLog(#"BViewController will disappear");
// Check whether the popoverViewController is visible
if (self.popoverController.popoverVisible==YES) {
[self.popoverController dismissPopoverAnimated:NO];
}
}
However, that is not being called directly by the framework as BViewController is inside a NavigationController. Hence, I register a UINavigationControllerDelegate with my NavigationController and implement the following two methods:
- (void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
// Pass the message on to the viewController in question
[viewController viewWillAppear:animated];
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
// Pass the message on to the viewController in question
[viewController viewWillDisappear:animated];
}
However, it seems that the passed in viewController parameter in both methods is the one that is about to be shown. I would have expected that the second method gives me access to the one that is about to disappear. So, when the user hits aforementioned button viewWillDisappear gets called on AViewController (which is about to be shown) and not on BViewController (which is about to disappear). Does that sound right? The apple documentation refers in both cases to
The view controller whose view and navigation item properties are being shown.
...which is not quite clear, I think. Thank you for some help, guys.
The two delegate method are both called for the same action (showing a view controller). The navigationController: willShowViewController:animated: is called before the new view controller is visible in the gui. The navigationController:navigationController didShowViewController:animated: is called after the new view controller is shown.
You will find this pattern in a lot of delegate protocols from apple. Unfortunately you do not have a delegate method in the NavigationViewController which tells you if the action was a pop or push.
I hook in my own protocol, which will know about the TO and FROM sides:
NavigationControllerDelegate.h:
#protocol NavigationControllerDelegate <NSObject>
#required
-(void) navigationController: (UINavigationController*) navController
willMoveFromViewController: (UIViewController*) from
toViewController: (UIViewController*) to;
#end
Instead of the regular UINavigationViewController, I then use a little helper class which keeps track of the view controllers:
NavigationHandler.h:
#interface NavigationHandler : NSObject <UINavigationControllerDelegate> {
NSMutableArray* m_viewControllers;
}
In my app delegate, I create one of these objects and set it as the delegate of the navigation controller:
...
m_navigationHandler = [[NavigationHandler alloc] init];
navigationController = [[UINavigationController alloc] initWithRootViewController: mainMenuViewController];
navigationController.delegate = m_navigationHandler;
...
And from then on its a simple case of comparing my own list of view controllers with what the navigation controller has:
NavigationHandler.m
#import "NavigationHandler.h"
#import "NavigationControllerDelegate.h"
#implementation NavigationHandler
-(id) init {
if ((self = [super init])) {
m_viewControllers = [[NSMutableArray alloc] init];
}
return self;
}
-(void) dealloc {
[m_viewControllers release];
[super dealloc];
}
- (void)navigationController:(UINavigationController *)navController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated {
// Find out which viewControllers are disappearing and appearing
UIViewController* appearingViewController = nil;
UIViewController* disappearingViewController = nil;
if ([m_viewControllers count] < [navController.viewControllers count]) {
// pushing
if ([m_viewControllers count] > 0) {
disappearingViewController = [m_viewControllers lastObject];
}
appearingViewController = viewController;
[m_viewControllers addObject: viewController];
} else if ([m_viewControllers count] > [navController.viewControllers count]) {
// popping
disappearingViewController = [m_viewControllers lastObject];
appearingViewController = viewController;
[m_viewControllers removeLastObject];
} else {
return;
}
// Tell the view that will disappear
if (disappearingViewController != nil) {
if ([disappearingViewController conformsToProtocol: #protocol(NavigationControllerDelegate)]) {
if ([disappearingViewController respondsToSelector: #selector(navigationController:willMoveFromViewController:toViewController:)]) {
UIViewController<NavigationControllerDelegate>* vcDelegate = (UIViewController<NavigationControllerDelegate>*)disappearingViewController;
[vcDelegate navigationController: navController willMoveFromViewController: disappearingViewController toViewController: appearingViewController];
}
}
}
// Tell the view that will appear
if ([appearingViewController conformsToProtocol: #protocol(NavigationControllerDelegate)]) {
if ([appearingViewController respondsToSelector:#selector(navigationController:willMoveFromViewController:toViewController:)]) {
UIViewController<NavigationControllerDelegate>* vcDelegate = (UIViewController<NavigationControllerDelegate>*)appearingViewController;
[vcDelegate navigationController: navController willMoveFromViewController: disappearingViewController toViewController: appearingViewController];
}
}
}
#end