I have a complex flow between view controllers (vc). When my main view controller shows, it checks if a user is logged in, and if not, it pushes a log in vc in viewWillAppear. The login then can the push a register vc. Further, on app's very first start, it also shows a special vc, also from the main vc's viewWillAppear (just once). It does not matter if the user has logged in or skipped the log in.
The app can be opened also by custom URLs, which should open one of my vcs. The app must start/resume at that screen without any visble transition between vcs. All this flow is handled from my main vc, which handles this in viewWillAppear and in didBecomeActive which is an observer for app's UIApplicationDidBecomeActiveNotification. Some of those custom URLs must open the login screen first.
My naive approach was to handle all this logic in my main vc's viewWillAppear where I pushed vcs base on the current state. This works for pushing the login screen on app's start without any problem. The problem is that when I return from the login and I need to push another vc. The main navigation controller knows that something was pushed (the back button becomes visible) but the old vc is visible (the main), with partially broken views, and does not react to touch events. Tapping the back button makes a complete mess from my app.
I googled and the problem seems to be that we cannot push a vc while another one is popping. I have found a BufferedNavigationController, which solves this, but it does not work correctly under iOS7 yet. I do not even see any logs in device's console.
Another issues is that while the app is in the background a memory warning could mess my vc's and the app has completely different startup than a normal resume from the background.
At the moment I came with a quick hack where I create a custom backstack for all the situations and set it as the navigation controller's back stack and push the last vc on top of that stack. Then in the login/register screens I modify the backstack if a user skipped the login/register process. This is an ugly hack an not a very future proof solution. I would like to centrally controll the pushing of vcs in one place and there cannot be any visible transition between them in that situation.
Is there any better/more robust solution to achieve this? I cannot have visible transition when returning back from one vc (this is animated) and the vc beneath it should push another on top of it while it becomes visible (no animation of this push). So it looks like we return to a completely different screen? No matter if it can be confusing for users, that is for another question.
EDIT 1: I am targeting iOS7 and newer only.
EDIT 2: Here is a sample demo which shows my issue. I directed the link to he main vc, which pushes other vcs in its viewWillAppear (the other vcs are not important). It is just for presentaion purposes.
This demo shows logs into console "nested push animation can result in corrupted navigation bar" and "Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted." which do not show up in my big project. I know what they mean, but how can I push a vc when antoher is popping away? The mentioned BufferedNavigationController does not work for me on iOS 7 (and it is not a problem, that it does not use ARC).
EDIT 3: When I start the provided demo for the first time, it will open the login screen instead of the main - good. Then after tapping the login it goes back to the main screen and should instantly push the another screen without the main being visible. It does not do that, the main is visible but the navbar thinks there is something above the main, thus the visible back button. It corrupts the backstack...
https://stackoverflow.com/a/20947860/1405008
Refer this for your first scenario like open login or other view controller.
For custom URL u can also use the same logic and check and present when its arrive from custom URL.
Always set the root view Controller will the easiest way to do this kind of different navigation stuffs in Your application also this approach avoid unwanted view-controller in stack.
Related
I have been attempting to create a walkthrough for my app although I also have a tab view controller which is the initial view controller. I have been able to identify when a user is opening the app for the first time, but when I make the walkthrough view controller initial, I get a Sigbart error. This is because I set up my tabbar in the app delegate.
Is there a way to possibly keep the tab bar VC the initial and hide the first VC if it is the users first time opening the app?
Is there another way of doing it?
I dont know the code to check of its the flrst time a user opens an app, but why dont you make that check on your tab bar controller? And then lf lt ls the first time, you just change the root vc to the tutorial vc. When they are done with the tutorial you just change back the root vc to the tab bar.
There easiest way to accomplish the tutorial-like behavior for new users is:
1) Make a new View Controller be the initial one.
2) Add code to check if its the first time the user launched the app. If it is, show the tutorial, if its not, show your tab view controller.
3) You can fill this "fake initial" view controller with the same image shown in the splash screen. This way the user will feel its just the splash one.
*) An added benefit of this approach is that you can check other useful things. For example, if your app has some kind of login feature you can manage it here skipping the login window for users who have already logged in. It can also be used to update your app's resources in case you are retrieving them from a server.
I am currently working on an Xamarin iOS application that has some navigation issues.
What i am currently trying to achieve is to push a ViewController while at the same time popping the previous one off. The reason for this is that I have one ViewController that shows some specific questions from this view controller i can spin up another ViewController of the same type and the user can fill in some more questions. What i want to be able to do is pop the previous filled in view controller so when the user hits the back navigation it takes me back to my home view controller (and not through all the previous instances of the question controller).
I also can't use the PopToRoot or PopToViewController.
I have tried doing a pop without animation and a push with animation and results in a quick flicker of the underneath viewController which is colourful so its a bit of a shock to the eyes.
The other thing I have tried is to manipulate the navigation stack underneath by pushing on the questionController and then removing from the stack the old viewController the problem with that is i have some tear down on the ViewDidDisappear (i work out if its a pop disappear rather than just a cover up) which doesnt seem to be called because of the manual removal. So along this method is it ok to call the ViewDidDisappear to carry out my unwiring so there is no leaks?
Many Thanks for your time reading.
Say, I have a Login screen, Contacts screen and Settings screen.
Login screen doesn't seem to be used frequently in the app so I suspect it is fine to exclude it from the main navigation flow.
Thus I set Login screen as the app entry point, then put all the rest screens as the children of the Navigation Controller. This way I do login process, then pass the control to the Navigation Controller and it does all the in-app navigation in a regular manner.
I am concerned if I am doing it right way, as probably I should make the Navigation Controller as the entry point and then dispatch all the navigation, even for Login screen, with it.
Is it supposed to have a single Navigation Controller inside an app? If so, is it supposed to have it as the parent dispatcher for all the scenes or is it fine to have some scenes put independent?
Here is the current config:
It's fine to embed multiple navigation controllers. In your case, I wouldn't think there would be any issues with not having the initial controller embedded in a nav controller-- getting back to the log in screen would be as simple as using an unwind segue. Alternatively, making the first VC a nav controller also presents no problem, but in your case it would eliminate the need for a second nav controller, based on your current configuration. I find multiple navigation controllers most useful when I'm using container views, and want to swap out VCs within that container.
That's my 2 cents.
I cannot find the answer to this although I have implemented this rather a few times already, but maybe the wrong way.
Say I have an App for iOS, it has a main screen, which goes to a list, that list has a < back (to main) and an add button. Now when I click < back, I go back to main as that's the pop() from the stack. No issues so far.
Now when I click the add button, that is added to the stack as well; when I click back on that screen I go back to the list which is fine.
The problem is; when I save the new item, I want to go to the detail screen, but I don't actually want to have the add screen on the stack anymore while it will be there. I want the < back button for the detail item pointing to the list.
I know how to do this, but what is actually the best to implement this with the navigation stack?
Well for adding elements the best practice is to present an ModalViewController.
In this way it is not added to the stack.
Update
Let's take as examples simple apps that apple provide with iOS, Contacts app. When you want to add a new contact a VC is presented.
You'll need to implement "Done" or "Save" button that will dismiss the modalViewController and if you want to take the user into detail screen you could post a notification or other mechanism on dismissViewController method's completion block that will push the detail page from the list. But be careful on animations if you dismiss the modal VC animated and push the detail page animated you could get some unexpected behaviour. My proposal is to dismiss the Modal VC animated and push the detail page without animation.
You can override UINavigationController's viewControllers property after you pushed detail view controller. Just get current viewControllers property array, iterate and find the one you don't want to see and remove it. Remember to set viewControllers array again.
https://developer.apple.com/library/ios/documentation/Uikit/reference/UINavigationController_Class/index.html#//apple_ref/occ/instp/UINavigationController/viewControllers
I have a couple views that come before I want to show my split view, disclaimer and then login. After successful login I want to segue to the the split view controller. However I do not think there is a way in storyboards to segue to a split view controller. How do I get from a normal view into my split view controller.
Sometimes if it seems to cumbersome it may be an indication that one need to look at the problem from a different angle.
Assuming your goal is that of forcing the user to go through the login process,
this is what I would do:
Have your splitView as the default view controller, added in the storyboard and loaded as the app starts
As soon as app is loaded, check for the existence of the user's credential. If you don't find any,
present your login framework modally (full screen to cover any data underneath).
Once the user has successfully logged it, dismiss the modalVC and you will have the underlying splitVC underneath ready for use.