Xamarin iOS applicationWillEnterForeground should call an viewController - ios

I have two viewController in my Xamarin.iOS app:
- loginViewController
- mainViewController
On every start or resuming the user have to login.
I think I have to use the WillEnterForeground function in the AppDelegate.cs.
But how can I call there the ViewController?
RootViewController root = new RootViewController(application.Handle)
Window = new UIWindow(frame: UIScreen.MainScreen.Bounds);
Window.RootViewController = root;
Window.MakeKeyAndVisible();
That is all I found on my internetresearches..

As you are using a UINavigationController to control the navigation of your app, at WillEnterForeground you could call:
this.Window.RootViewController.NavigationController.PopToRootViewController(true);
Considering the fact that a UINavigationController is your root view controller.

Related

viewWillAppear and later lifecycle events not called when UIViewController is set as root of UIWindow during launch

I have the following piece of code in my application(_:didFinishLaunchingWithOptions:) method
self.window = self.window ?? UIWindow()
self.window?.makeKeyAndVisible()
let mainViewController = HomeViewController.instantiate() //here I just call the viewController through storyboard, set some properties and return its instance
window?.rootViewController = mainViewController
I checked and the only lifecycle events that are called when the viewController is added to the window are:
viewDidLoad
viewWillLayoutSubviews
viewDidLayoutSubviews
Anything after that is not called (this happens only when it is initially added in the window, after that the events are called as they are supposed to). Safe Layout Guide is also not set (and I believe the same goes with other view properties). In order to bypass this issue I do the following:
window?.rootViewController = UIViewController()
and then
window?.rootViewController = mainViewController
It seems that this somehow forces the window to work correctly the second time it has its root view controller set.
However it seems that either there is something I am missing or there is some kind of lifecycle issue.
Has anyone faced the same issue and found the underlying cause?
Try making the window key and Visible at the end after setting the root view controller of window.
self.window = self.window ?? UIWindow()
let mainViewController = HomeViewController.instantiate()
window?.rootViewController = mainViewController
self.window?.makeKeyAndVisible()

UIViewController not added to the view controller hierarchy after performSegueWithIdentifier

I have the following method which functions properly when the app is in foreground. The SecondViewController gets pushed to the top of the UINavigationController's view controller stack.
I have two UIViewController's FirstViewController and SecondViewController in a UINavigationController and FirstViewController is the rootViewController of the UINavigationController
1. UINavigationController *navigationController =
(UINavigationController*) self.navigationController;
2. [self.navigationController
performSegueWithIdentifier:#"showSecondView" sender:self];
3. SecondViewController * secondController = (SecondViewController*)
navigationController.viewControllers[1];
4. [secondController performSomeAction];
After pushing the SecondViewController, I pop to UINavigationController's rootViewController which is FirstViewController. This code works fine when the app is in the foreground.
I have an iOS8 interactive notification calling the FirstViewController, which pushes the SecondViewController using performSegueWithIdentifier.
Interactive Notification correctly calls the FirstViewController but the UINavigationController controller does not push the SecondViewController(see above code), even though the code correctly when the app is in the foreground.
Some Initial Investigations I did
Segue Identifier is correct
When I put the breakpoint on the fourth line on the case where the UINavigationController does not push the SecondViewController, secondController instance is a SecondViewController.
breakpoint showed that the UINavigationController only had FirstViewController even after the performSegueWithIdentifier.
No memory leak in instruments
Any help is appreciated!

Login View Controller over modal View Controller

The following scenario:
My iPad app has a SplitViewController as it's main VC. After starting the app (new or from background) I have a fullscreen login view that (obviously) disappears after entering the correct password.
The problem:
After login, I want to present the exact same screen that was there BEFORE moving to background. This works fine UNLESS there is a modal view on top of the split view (like settings etc).
What I tried:
In AppDelegate I store my self.window.rootViewController, make the login vc as my root vc and after login I set my stored root VC as actual root VC. But then the (modal) settings view is not visible and can not be opened again (Warning: Attempt to present VC on SplitVC which is already presenting VC). In fact, no other modal view can ever be opened (unless app is properly closed).
Second try: Instead of setting the login VC as root VC I presented it as a fullscreen modal view on top of my split view. This yielded the same error message as the first try but a different result. After entering background mode the login VC won't be presented at all (since there already was a modal view).
This is b'coz you R trying to present the VC while it is actually loaded as RootViewController .
Try using this :
UISplitViewController :
Once loaded the Root and as well as MasterViewControlller , You will make the UIViewControllers as SubViews for Your
RootViewController.. From the UIViewController , If you want to revert
back to the RootViewController , Try this :
[self.navigationController popToRootViewControllerAnimated:NO];
I just thought your problem is similar.
Hope it helps.
What I did was the following:
Create a property that can store my modally presented VCs (they are all embedded in a UINavigationController)
#property (nonatomic) UINavigationController *navController;
When creating the login vc I store my modal vc (which may be nil which is fine), dismiss it and present the login vc
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil];
LoginViewController *loginViewController = [storyboard instantiateViewControllerWithIdentifier:#"LoginView"];
self.navController = (UINavigationController *)self.window.rootViewController.presentedViewController;
if (self.navController) {
[self.navController dismissViewControllerAnimated:NO completion:nil];
}
[self.window.rootViewController presentViewController:loginViewController animated:NO completion:nil];
And when the login is successfull I dismiss the login vc and restore the modal vc (if available)
if (self.navController) {
[self.window.rootViewController presentViewController:self.navController animated:NO completion:nil];
}
Can you try this way.
Root VC is main screen not login page.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Inside the above code, present your login page to your root VC.
- (void)applicationDidBecomeActive:(UIApplication *)application{
//Use below method or similar method to remove any presented VC on Root VC
if ([((UINavigationController *)self.window.rootViewController).visibleViewController isKindOfClass:[RLSplashViewController class]]) {
NSLog(#"AppDelegate dismiss splash page");
[self.window.rootViewController dismissViewControllerAnimated:NO completion:nil];
}
//And present your login VC
}
Inside the above code, First and very import, remove any VC that presented on the root VC. And then present login page to your root VC.
I am currently using this way to present splash page(from background or new), in your case, it is login page. Hopes this is helpful for your case : )

Using Split View Controller and Navigation Controller As Window Root View Controller Alternately

i have a navigation controller which is my application's root view controller. it is my main screen. I have a split view controller whose master view lists location and detail view shows the location on the map. I wanted to push the split view controller to the navigation controller, but it throw a error saying split view controller cant be pushed to navigation controller and it must be application's root view controller.
So i tried a hard way.
MyAppDelegate *myappdelegate= [[UIApplication sharedApplication] delegate];
UISplitViewController * vc= [[UISplitViewController alloc] init];
vs.viewControllers = [NSArray arraywithObjects......
....
myappdelegate.window.rootViewController= vc;
This works. It shows split view controllers without animation as expected. And i do the same steps when i was closing split view controller. i am creating a navigation controller with main screen nib and setting this my app delegate's window.rootviewController again.
And it succesfully loads main screen again.
But i suspect that this is the proper way of achieving this. Is there more eligible way of doing this?
And i couldnt release split view controller's child controllers. i made a breakpoint on my release method of child controllers. it wasnt be catched. I assumed that when i set my app's root view controller as navigation controller, the old root view controller (split view controller) must be released along with its childs.
Then i tried below code when closing split view controller.
UISplitViewController *oldviewcontroller= (UISplitViewController*) myappdelegate.window.rootViewController;
for (UIViewController *child in oldviewcontroller.viewControllers)
{
[child release];
}
//Setting the navigation controller as window.rootviewController again in here.
This code throw an error saying "[UIImageView __viewDidDisappear:]: unrecognized selector sent to instance 0x7d...."
i think because of releasing the view already, there is no imageview on viewdidDisappear event.
In brief, my question is that am i using right method to achieve this? If so, how can i successfully release all child view controllers?
I finally found a way. I probably found the error. What i have done is cutting the branch on which i am sitting. I was releasing the view controller which i am currently in:) When viewdidDisappear called, there is no such view controller. Because i throw away it to space already.Below is my working steps. I hope it will be useful to someone. But i cant stand thinking of apple may reject my app. I wish finding a suitable way.
This is my working ultimate way of using split view controller and navigation controller as window root view controller alternately.
Firstly i defined NavigationController and SplitViewController property in AppDelegate interface.
AppDelegate.h
#property (assign,nonatomic) UINavigationController * NC;
#property (assign,nonatomic) UISplitViewController *SVC;
Secondly i assign newly created NC on AppDelegate didFinishLaunch event.
AppDelegate.m
//Creating my main screen controller
//Creating my navigation controller with my view controller instance. Then
self.NC= my_navigation_controller;
self.window.rootViewController= self.NC;
Thirdly creating a splitview controller and setting as app's root view controller
MyMainScreen.m
-(void) OpenSplit()
{
//Creating my master view controller of SVC
//Creating my detail view controller of SVC
//Creating my SVC;
AppDelegate * app_delegate= [[UIApplication sharedApplication] delegate];
app_delegate.SVC= newly created my SVC;
app_delegate.window.rootViewController= app_delegate.SVC;
}
Fourthly releasing unused NC in viewDidLoad event of detail view of SVC.
MyDetailView.m
- (void) viewDidLoad()
{
...
AppDelegate * app_delegate= [[UIApplication sharedApplication] delegate];
app_delegate.NC= nil; //i dont need it now. i am releasing. Releasing Navigation Controller release as well child controllers. I learned with testing.
}
Fifthly managing close split view function.I used UIBarButton on NavigationBar in DetailView.
MyDetailView.m
-(void) closeSplitView
{
//Creating navigation controller with my main screen view controller
AppDelegate * app_delegate= [[UIApplication sharedApplication] delegate];
app_delegate.NC= newly_created_NC;
app_delegate.window.rootViewController= appdelegate.NC;
}
Sixthly handling unused split view controller in Main Screen viewDidLoad event.
MyMainScreen.m
-(void) viewDidLoad
{
AppDelegate * app_delegate= [[UIApplication sharedApplication] delegate];
app_delegate.SVC= nil; //I am releasing it because i am working with NC now.
}

UINavigation pushing a new root controller

I am trying to push a new root controller to a navigation stack, but using a side reveal menu.
My app delegate has the following:
welcomeViewController = [[MyWelcomeViewController alloc] initWithNibName:#"MyWelcomeViewController" bundle:nil];
navController = [[UINavigationController alloc] initWithRootViewController:welcomeViewController];
navController.navigationBarHidden = YES;
// Then we setup the reveal side view controller with the root view controller as the navigation controller
self.revealSideViewController = [[PPRevealSideViewController alloc] initWithRootViewController:navController];
[self.revealSideViewController setDirectionsToShowBounce:PPRevealSideDirectionNone];
[self.revealSideViewController setPanInteractionsWhenClosed:PPRevealSideInteractionContentView | PPRevealSideInteractionNavigationBar];
// Then we make the window root view controller the reveal side view controller
self.window.rootViewController = self.revealSideViewController;
Once the welcome view controller is displayed, the user logs in. Once logged in the following process runs again from the App Delegate.
self.navController.navigationBarHidden = NO;
[self.navController setTitle:#"Home"];
[self.navController pushViewController:homeViewController animated:NO];
I then have a side view controller setup which is a table view with custom cells setup.
When a row is selected I need to push a new root controller onto the navigation controller. I try this by using the following in the table view for the cell selected.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[self.navigationController setViewControllers:[NSArray arrayWithObject:accountViewController] animated:NO];
Unfortunately this does not do anything. If I add the code to the App Delegate and then call the method from the table view controller then it works, however not from the .m file for the table view itself. Adding a log I can see the above is run, just does not do anything.
I am unsure if I need to do anything different on the above. For example, completely pop the views currently shown, then create the navigation controller and PPRevealSideViewController all over again. If I am supposed to, I am unsure how to pop all the current views to then push the new to the window, not from the AppDelegate.
The reason I do not want this in the App Delegate is because it is the incorrect way to approach this, and I would then need a separate method for each new root controller I would like to push from the menu, so the App Delegate would become very large.
Check UINavigationController.h:
#interface UIViewController (UINavigationControllerItem)
#property(nonatomic,readonly,retain) UINavigationController *navigationController; // If this view controller has been pushed onto a navigation controller, return it.
It means when you do myViewController.navigationController you will either get nil if myViewController is not pushed to any navController or the navController reference myViewController is pushed into.
As I understand your tableViewController is not pushed into the navController stack, that means you can't get the navController with tableViewController.navigationController. Instead you'll need to use anyViewControllerInTheStack.navigationController or if the navController is the rootViewController of your keyWindow, by
((UINavigationController*)[[UIApplication sharedApplication] keyWindow].rootViewController)
Add something like this to your AppDelegate.h:
#define XAppDelegate ((AppDelegate *)[[UIApplication sharedApplication] delegate])
Now you can access any iVar of AppDelegate from any .m file in your project.
MyAccountViewController *accountViewController = [[MyAccountViewController alloc] init];
[XAppDelegate.navController pushViewController:accountViewController animated:NO];
Make sure you add the correct imports.
One more thing: It's good to pop the login window from your navcontroller once you are done Logging in.
Hope this helps.

Resources