Passcode ViewController Presentation from Modal View - objective-c

I'm implementing a Passcode feature in my iPhone app which has a UITabBarController as a root view controller. I have everything working great in most situations, by displaying a modal Passcode ViewController from the tabBarController when the app goes into the background, like so:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if ([[NSUserDefaults standardUserDefaults] valueForKey:kPasscodeStringKey]) {
PasscodeEntryVC *passcodeView = [[PasscodeEntryVC alloc] init];
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:passcodeView];
[tabBarController presentModalViewController:nav animated:NO];
}
}
My problem comes when the app is already displaying a modal view controller when it enters the background. Then, no passcode view appears. What would be the correct way to do this? Instead of just sending the message to the tabBarController to present the view, should I be checking first to see what the current view is, then have that present the passcode? If so, how is this done? Thanks.

First - you are leaking memory because you do not release your passcodeView and navigation controller nav.
Second - you could keep a simple BOOL variable that is updated whenever a modal view is presented or dismissed. If there is a modal view, just call dismissModalViewController:animated: in your applicationDidEnterBackground: method.
You could also check the frontmost view controller with [self.navigationController.topViewController class], but I have found this to be unreliable.

What I usually do is to ensure that any views I have that may present a modal view controller to dismiss the modal view controller whenever it is sent the UIApplicationWillResignActiveNotification notification, while over in my app delegate, I set it up exactly like yours.
One caveat though, is that whenever you dismiss the said modal view controllers, you need to ensure that you dismiss them with animated: set to NO before presenting your passcode view controller.

Related

UIViewController auto rotate?

I setup my UIViewController in app delegate as self.window.rootViewController. For some reason I need open sign view as modal from my root view controller like:
self.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
[self.window.rootViewController presentModalViewController:signViewC animated:NO];
If I change device orientation in sign view, my root view controller doesn't change orientation and I get incorrect orientation after sign view dismissed. Is it possible to change root view orientation if I changed orientation in sign view?
In my root controller I added:
- (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
And autorotation works if I rotate root view without modal view.
First of all, you shouldn't be presenting a modal from your app delegate. Whatever check you are doing to determine if you should present that view controller should be done in your main view controller. From there, you should call the code to present the modal, like so:
[self presentModalViewController:signViewC animated:NO]
That way, your main view controller isn't dismissed, it's only pushed down the view stack and should still respond to rotation notifications.
Try that out and let me know how it turns out.
EDIT
Take out the ModalPresentationStyle. That's what's messing you up.

Should I use the same detail ViewController to work both modally and when pushed?

This seems to be the patten used throughout Apples applications; Creation of a new record is done through a modal View which needs to be saved or canceled to continue, and editing a record is done through a view pushed onto the navigation stack.
It doesn't seem right to be basically duplicating my ViewController for 'add' and 'edit' but there are several differences in how pushed and modal ViewControllers work which complicate things.
How should I be doing this so it can cover both bases?
-
Differences include.
When pushed onto the stack the navBar appears at the top of the View and can be configured to contain the cancel/save buttons. When presented modally this is not the case so to duplicate the interface a toolbar needs to be created separately and close/save buttons added to this instead.
When dismissing a pushed view we send a message to the navigation controller [self.navigationController popViewControllerAnimated:YES];, when dismissing a modal view we send a message to self [self dismissModalViewControllerAnimated:YES];
You could add the UIToolbar in InterfaceBuilder, and then just hide it in viewDidLoad when self.navigationController is not nil.
As for dismissing, you could have something like:
- (void)didCancel {
[self.navigationController popViewControllerAnimated:YES] || [self dismissModalViewControllerAnimated:YES];
}
This will shortcircuit if your viewcontroller is part of a navigationcontrol, and use dismissModalViewControllerAnimated otherwise.
This should work for your cancel button. For your save button, it is useful to call some sort of delegate method such as:
- (void)didSave {
// do your saving juju here
if([self.delegate respondsToSelector:#selector(viewController:didSave:]) {
[self.delegate viewController:self didSave:whatJustGotSaved];
}
[self.navigationController popViewControllerAnimated:YES]; // noop if currently modal
}
In the delegate's implementation then, you can put:
- (void)viewController:(UIViewController*)viewController didSave:(NSObject*)whatJustGotSaved {
// do stuff with parameters
[self.modalViewController dismissModalViewControllerAnimated:YES]; // noop if not modal
}

NSNotificationCenter for presenting multiple modals?

I have an app delegate, whose default view should be preceeded by a modal view controller, and sometimes by two modal view controllers. So in the app delegate's didFinishLaunchingWithOptions, I'm checking if there is need for, and in that case displays, the first modal view controller.
Upon dismissing the first modal view controller (using [self dismissModalViewControllerAnimated:YES];), I may want to display the second modal view controller. This is known by the app delegate as well.
So my solution was to use NSNotificationCenter to tell the app delegate that the first modal view controller now have been dismissed. When that happens, the second modal view controller can be displayed by the app delegate, if it is needed.
It works fine, but is there a cleaner solution? I think NSNotificationCenter is really ugly stuff.
Note on displaying multiple modal view controllers at once
I did try to display the first AND the second modal view controller inside of didFinishLaunchingWithOptions, but I never got it working. Here's what I tried:
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[window setRootViewController:tabBarController];
[self.window makeKeyAndVisible];
[tabBarController presentModalViewController:pinViewController animated:NO];
if([self needsActivation]) {
[tabBarController presentModalViewController:activationViewController
animated:YES];
}
}
UPDATE: The above code works with the following correction:
if([self needsActivation]) {
[pinViewController presentModalViewController:activationViewController
animated:YES];
}
In this particular case, there was no need for NSNotificationCenter, as I thought. I had tried to display multiple modal view controllers, but I'd made an error.
When displaying the modal view controller B from the modal view controller A, it works fine. I had tried presenting modal view controller A and B from a parent view controller.
So when presenting modal view controllers in a hierarchy instead, there is no need for NSNotificationCenter. The view controllers are dismissing themselves, animations works and I'm a step further towards bending the UIKit to my will.
I've edited the code in my question, which now works fine.

ios sdk only one view changes at a time bug

I'm pushing a number of views:
the top one is a UITabBarController
the second one is a UINavigationController with a pushed view
the third one is a modal box.
Once the close button in the modalbox is pressed I'm trying to revert everything to the default state and change the tabbar index.
[self dismissModalViewControllerAnimated:YES];
[self.navigationController popViewControllerAnimated:NO];
[self.tabBarController setSelectedIndex:3];
This dismisses the modal view but doesn't do anything else. Any ideas what could be wrong? I read something about a possible ios bug but I don't know how to work around it.
Neither UITabBarController nor UINavigationController is a view. Both are subclasses of UIViewController and have a property NSArray *viewControllers.
If you have an actualView controlled by an ActualViewController that is pushed on top of a rootView controlled by a RootViewController that is the rootViewController for the navigationController, and you also have a modalView controlled by a ModalViewController, then put
[self dismissModalViewControllerAnimated:YES];
in ModalViewController.m, and put
[self.navigationController popViewControllerAnimated:NO];
in ActualViewController.m (from whence modalView is pushed, presumably), and put
[self.tabBarController setSelectedIndex:3];
in RootViewController.m (from whence actualView is pushed, presumably).
If modalViewController was never added to the navigationController, then it doesn't know that the navigationController exists.
If actualViewController was never added to the tabBarController, then it doesn't know that the tabBarController exists.
The easy (and dirty) way:
Dismiss the modal view in the modal view. Make the navigation view controller the delegate of the modal view. Make the tabbar controller the delegate of the navigation controller. When the button is pressed call a method in the navigation controller that pops the view and calls a method of the tabbar controller which changes the selected tab.

loading a UINavigation controller from a UIView

i am designing an app that has a login screen. it is a UIView with username/password and a button to submit. once the user authenticated successfully, i want to load a new xib file that holds a navigation controller and a navigation bar. below the bar i want to load a tableView and switch between other views as i move along with the programming of it.
what i did is create a new class that inherits from UINavigationController and assembled the xib file to include the navigation controller. i hooked it back up to file's owner and i'm loading the navigation controller modally like this:
myNavController* navVC = [[myNavController alloc] initWithNibName:#"navXibFile" bundle:nil];
[self presentModalViewController:navVC animated:YES];
[navVC release];
this works okay as the navigation controller shows up. however, it shows up with no title, even though i've set one up in IB. moreover, the tableView's delegates are hooked up via IB but i cannot even see empty lines. all i see is an empty navigation bar at the top and blank view (one piece) below it.
thank you for your help.
so i figured it out... first it's a design decision right? is the app being managed by a navigation controller? if so (which is my case), expect the main (first) view, that is a login page, all you need to do is to hide the navigation bar in your ViewdidLoad for the main view:
[self.navigationController setNavigationBarHidden:YES animated:YES];
once the user logs in and you push the next view like this:
MainTableViewController* mainTableVC = [[MainTableViewController alloc]
initWithNibName:#"MainTableViewController" bundle:nil];
[self.navigationController pushViewController:mainTableVC animated:YES];
[mainTableVC release];
and lastly, in the ViewDidLoad of the next view controller:
[self.navigationController setNavigationBarHidden:NO animated:YES];
in case your app needs a navigation controller for a specific section of the app but not all if it, you will need to use the VC to manage this, in a similar way the appDelegate manages the sample navigation based sample app.
i hope this helps anyone struggling with wrapping their minds around the design patterns implemented here.
cheers.