Modal UINavigationController hides although not dismisses - objective-c

Okay, so I'm building an universal iOS app with an initial login view (view controller named LoginVC), just a plain simple UIViewController. If the login is successful the app segues to an navigation controller (MainNavigationVC). I created this segue through the storyboard gui of XCode, so no programmatic creation of the nav controller is done. The nav controller is presented modally in fullscreen, so the rest of the app is run atop the login view, with this nav controller as the centerpiece of everything.
The navigation controller contains a view (with a view controller named UserStartPageVC), and in its navigation bar is a logout button. This button sends an target action to UserStartPageVC, with the goal of dismissing the nav controller thus bringing the user back to the login view.
So far everything works fine. I can login and use the app as intended. But! When I log out and then re-login XCode tells me this:
Warning! Attempt to present <MainNavigationVC: 0x753110> on
<LoginVC: 0x756fcf0> while a presentation is in progress!
I suppose this means that the login view is trying to modally display a MainNavigationVC navigation controller, but another one is already displayed, right? But how? Can a view be presented without showing?
And how can I get rid of the old nav controller when logging out? I've tried several ways of dismissing the modal view, for instance:
from within UserStartpageVC running
[x dismissViewControllerAnimated:YES completion:NULL]
[x dismissModalViewControllerAnimated:YES]
where x is either self, self.parentViewController or self.presentingViewController.
setting the LoginVC as a property in UserStartpageVC and running
[self.loginVC dismissViewControllerAnimated:YES completion:NULL]
and so on.
All of the tested calls actually brings me back to the login screen, so it's kind of working.
Any ideas? Relevant code samples can be provided if necessary, I just couldn't figure out which pieces that were of interest. The seguing to the navigation controller has no code (except for a performSegueWithIdentifier:sender:), and the code for dismissing it is the part I cannot seem to get straight.
As a sidenote. So far this isn't a REAL problem; the app runs, and it IS possible to logout and re-login without any other side-effects than an error message in XCode. But I suppose this will be a memory leak if users logout and login multiple times, and I'm not in the mood of an unnecessary rejection from Apple.

I discovered another way to get the exact same error message. Lucky me!
If you created a segue at one point and had it tied to a button (click button -> new view) and then later give that segue a name and invoke it directly using
[self performSegueWithIdentifier:#"identifierName" sender:self];
then you can get this error because you can effectively trigger the segue twice. I thought making the button invoke an IBAction would turn off the segue I had set up in the first place, but apparently not. Hitting the button triggered the segue twice, but after I deleted the segue and re-created it as a manual segue on the view with the same identifier then I was able to invoke it via the above code and not get the warning message.

Hoopla! My bad.
Seemed I had set up the notification observing from the login API call in a stupid way. For every time the user triggered a login (or re-login), it added itself as an observer for the notification; the result was that it performed one more segue for every time a login was done.
And doing multiple segues at the same time, is... well, obviously bad.

Related

UIKeyboardWillShowNotification - iOS8 vs iOS7

With iOS8, I noticed that a view controller was no longer receiving a UIKeyboardWillSHowNotification, when it previously was with iOS7.
Here's the scenario:
1.) View Controller A is displaying a keyboard, and pushes View Controller B without resigning first responder
2.) View Controller B has a control that becomes first responder during its viewDidLoad call, while it's being created by VCA, before it's pushed onto the nav controller
3.) If VC A is NOT displaying a keyboard when pushing B, the notifications work fine. However, if A is still editing when pushing B, then B does not get a keyboard will show notification.
Without the keyboard notification, VC B is not resizing / repositioning and does not look right.
The workaround I'm using until I find a solution is to do the following from any view controllers that might be editing when pushing another view controller that might be editing:
i.e., before pushing another view controller, be sure to call:
[self.view endEditing:YES];
While it works, it doesn't seem good that the view controller (B) can be 'broken' by the state of the app prior to displaying it.
Question: Am I doing something wrong here?
As far as I can tell, one of 3 things are possible:
A.) I should be getting the notifications, but I'm not b/c I'm doing something wrong
B.) I should be getting the notifications, but I'm not b/c of a bug
C.) I can't rely on always getting the notifications...But if I don't get the notifications in VC B when it appears, I need to be able to get the keyboard dimensions of the displayed keyboard without relying on the keyboard notification info. All the apple docs say to use the notifications though (as far as I can find).... which points back to options A.) or B.).
I can create and upload sample code later tonight / early tomorrow to try and isolate / for you all to test/reproduce to see what I'm doing.
I can see the same issue with iOS8 / xCode6 (works with iOS7 and xCode5). In my case, I'm observing a systemStatus property on the model in my AppDelegate so as to log the user out and bring the user back to the login screen when the user logs out from anywhere in the app. I'm doing that by setting the window.rootViewController to the loginViewController in my App Delegate observeValueForKeyPath: method.
This works fine on iOS7 / xCode5 but on iOS8 / xCode6, I loose the keyboard in the way. Looks like my loginViewController might be registering for keyboard notifications (in its ViewWillAppear method) before the window's rootViewController switch is complete (in iOS8) thus registering to the old window's notification center...
I moved the registration for keyboard notifications to the ViewDidAppear: method instead and that seems to fix it but somehow this seems to be called twice for some reason.

Present View Controller without calling presentViewController (or dismissViewController)

I am in the process of building a reasonably complex iPad app that will run on iOS7+. The app has a login screen that must be accessed once every user session, this is the starting view controller in my storyboard. The user can log out from any other screen in the app (there are about 60 other screens) by touching a button that is always available in every other ViewController. When the user logs out, a custom transition animation should be used.
It seems I can achieve this in one of two ways, either with a segue from every screen in the app to the login page, which makes the storyboard impossible to read, or presentViewController.
I've implemented this with presentViewController, by looking up the view controller by id from the storyboard (which creates a new instance, which is a desired behavior) and then presenting it from the current view controller.
Not surprisingly, this does not dismiss the original login view controller and essentially creates a stack of view controllers, eventually I run out of memory as each time a user logs out, a new login view controller is created and retained.
Is there a way to clear this "stack" of view controllers?
Is there a different way to present a view controller, with an animation, that does not involve presentViewController or segues? I've considered view controller containment, but that doesn't seem quite right when used with storyboards.
Have you considered replacing the root view controller? If the app delegate observes a logout notification and replaces the root view controller with the initial content of the storyboard that should get the app back to the initial screen.
I've seen that presented as a solution for login/logout issues elsewhere on the web, but I'm not sure if there's a transition you can animate there.
I think your approach is wrong. Correct me else wise.
Login mechanisms are supposed to be singleton instances. Hence you should define your view controllers, models, views all as singleton instances. Please look at my following code for example.
static id objectInstance;
+ (id) sharedInstanceID {
if (!objectInstance) {
objectInstance = [[YourClass alloc] init];
}
return objectInstance;
}
Let me know if this helps you.

Increasing number of living Views

I've set up a really simple project using storyboards including two views as shown here: http://i.stack.imgur.com/iRx21.png. The navigation can be done by either selecting a cell in the custom table view or hitting the back button labelled with "<<". Everything works fine except the following:
when I switch between the views, every time an instantiation happens. The profiling shows an increasing number of view objects. I would like to keep only one of each view and instantiation should be happen only once. What am I doing wrong? (I'm using ARC.)
Thanks in advance!
You should not link your back button to the parent view controller. This is what causes the new instantiation.
The way to go is to embed the table view into UINavigationController (in IB, choose Editor -> Imbed In -> Navigation Controller. Then change your segue to a Push segue. You can of course hide the navigation bar etc. to make things look exactly as you like. Then, link the back button to the controller with an IBAction and in the handler do a simple
[self.navigationController popViewControllerAnimated:YES];
This would be the appropriate logic of what you are doing. Of course, you can also push the web view modally and then handle the button click with
[self dismissModalViewControllerAnimated:YES];

UIViewControllers problems

Hi there and thank you in advice for your help. I have a really strange problem while working with ViewControllers in Xcode4. First of all I have to say that I'm not using storyboards and I prefer to create any UI element programmatically. So I've set a UIButton and I want that, when pressed, it brings me to a new view controller. This is the code I'm using for a button:
-(void)settingsAndExportHandle:(UIButton *)buttonSender {
SettingsViewController* settingView = [[SettingsViewController alloc] initWithNibName:#"SettingsViewController" bundle:nil];
settingView.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:settingView animated:YES];
}
This buttons is initialized and allocated in the viewDidLoad method of the RootViewController. I want to switch to the other view controller (in this case SettingsViewController) when I press the button.
The strange thing is that when I press the button, the animation that flips the controllers goes well, but when it finishes I obtain the EXACT same things that I had on the RootViewControllers (same custom views, same buttons, same all!). The question is: what I'm missing?? I have to say that I use ARC (automatic reference counting) so I can't release or dealloc the views and buttons I've created on my RootViewController.
Any help will be appreciated. Thank you all!
Pushing and and modally presenting view controllers does not deallocate the view controller that presented them. It simply adds the additional view controller to the stack. You'll need to implement a callback method so that when the user hits the button to flip back to root view controller, your settings view controller lets the root view controller know what's about to happen so you can call a method you've written to reset the interface back to whatever state you need it at. You may also be able to use viewWillAppear: but that's a little messy.
However, according to the Apple Human Interface Guidelines, the user expects that when they push a view controller or modally present it, the view controller they were on will save state and be exactly the way they left it when they came back. It's disconcerting and annoying when state is not preserved while in a navigation controller context. It is especially annoying when it's modally presented.
Think about this - A user is in a hypothetical Mail app. They start typing out an email and set a font size and a color. They tap on the add attachment button, which brings up a modal view controller that allows them to select a picture. They select the picture and the modal view is dismissed, and in your implementation, the mail composing interface would have reset and the email content would be gone or at the very least the selected font size and color would be back to the default. That's not a good experience.

MonoTouch: StoryBoarding - manual segues?

iPhone/iPad dev newb here...
I am using MonoTouch to create a universal iPad/iPhone storyboard app. In the primary view controller (RootViewController) the default auto-generated behavior is a table with a single cell "Detail" in it, which hardwires you to the next destination (DetailViewController).
I'd like to change this RootViewController to instead show a login control I've made extending UIViewController (LoginView). I have had some success putting the LoginView inside the RootViewController, but can't figure out how to make it 'segue' to DetailViewController. And upon watching how the iPad app works, where there is no segue (that I can see) between the two, am I going about this wrong?
To summarize: How do I enhance this storyboard app with a preceding login screen, reusing its contents/xib between the iPad and iPhone variant?
Apologies if this is a bit unclear and muddled.......
I have not tried a storyboard app in Monotouch yet, but I did spend a lot of time working out the best approach for a login screen for my application. What I ended up doing, which could work in your situation as well, is presenting the login screen as a Modal view. Once the user has successfully logged in, we then push the next appropriate view.
So if you are automatically pushed into the detail view by the storyboard viewcontroller, you could display the login dialog as a modal dialog from the detail view's DidLoad or DidAppear methods.