Abe
Abe

Reputation: 1929

Xamarin iOS present a different view controller when app returns from background

The xamarin ios app which I am working on requires GPS to be always enabled. When the application is first installed the user gets a prompt to enable GPS however the user has the option to turn off location services at device level.

I would like my app to prompt(i.e.. NAG) user to enable gps. Hence, I created a new viewcontroller and the corresponding xib file. The xib file looks like this

New View Controller

I have added the following code in the WillEnterForeground and there are no errors however the view is presented but if the gps is enabled this view is shown not the previously active view.

     public override void WillEnterForeground(UIApplication application)
            {
                Console.WriteLine("App will enter foreground");
                if (CLLocationManager.Status == CLAuthorizationStatus.Denied 
                    || CLLocationManager.Status == CLAuthorizationStatus.Restricted
                    || CLLocationManager.Status == CLAuthorizationStatus.NotDetermined)
                {

                    window = new UIWindow(UIScreen.MainScreen.Bounds);

                    var rootNavigationController = new UINavigationController();
                    rootNavigationController.PushViewController(new LocationServicesVerifyViewController(), false);
window.RootViewController = rootNavigationController;
                    window.MakeKeyAndVisible();

                }
            }

The idea is I will present this view when application is launched or when returns from background if Location services is not enabled.

Clicking on the Enable GPS Button will run the following code which will basically take the user to location section on the device.

            if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
            {
                NSString settingsString = UIApplication.OpenSettingsUrlString;
                NSUrl url = new NSUrl(settingsString);
                UIApplication.SharedApplication.OpenUrl(url);
            }

My class level declarations are

  // class-level declarations
        UIWindow window;

My current finished launching method looks like this

public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
    AppDelegate.Self = this;

    window = new UIWindow(UIScreen.MainScreen.Bounds);

    var rootNavigationController = new UINavigationController();
    window.RootViewController = rootNavigationController;

    DataAccess.CheckDatabase();

    if (DataAccess.GetLicence().APIKey == "")
    {
        rootNavigationController.PushViewController(new LicenceActivationViewController(), false);
    }
    else
    {
        rootNavigationController.PushViewController(new JobListViewController(), false);
    }

    window.MakeKeyAndVisible();

    return true;

}

Upvotes: 1

Views: 1287

Answers (1)

Ax1le
Ax1le

Reputation: 6641

Firstly, there's no need to create a new window when you want to present the LocationServicesVerifyViewController in your AppDelegate. You can get the current window's root view controller using: Window.RootViewController. Then present a new controller above that view controller.

Secondly, we should detect the type of current top presented view controller when the location state is denied. If it has been LocationServicesVerifyViewController, we should not present it again.

At last, when the user has changed the location state and returned back to the app. We can display the previous view controller through dismissing the current top presented view controller.

Here is my code for you referring to:

public override void WillEnterForeground(UIApplication application)
{

    if (CLLocationManager.Status == CLAuthorizationStatus.Denied
            || CLLocationManager.Status == CLAuthorizationStatus.Restricted
            || CLLocationManager.Status == CLAuthorizationStatus.NotDetermined)
    {
        var topViewController = GetTopPresented(Window.RootViewController);

        if ( !(topViewController is LocationServicesVerifyViewController))
        {
            LocationServicesVerifyViewController controller = (LocationServicesVerifyViewController)UIStoryboard.FromName("Main", null).InstantiateViewController("LocationServicesVerifyViewController");
            topViewController.PresentViewController(controller, true, null);
        }                
    }
    else
    {
        var topViewController = GetTopPresented(Window.RootViewController);

        if (topViewController is LocationServicesVerifyViewController)
        {
            topViewController.DismissViewController(true, null);
        }
    }
}

UIViewController GetTopPresented(UIViewController viewController)
{
    if (viewController.PresentedViewController != null)
    {
        return GetTopPresented(viewController.PresentedViewController);
    }
    else
    {
        return viewController;
    }
}

I define the LocationServicesVerifyViewController in the Main.storyboard, so the constructor of it is different. But it doesn't impact other stuff, you can change this part as you want.

Upvotes: 2

Related Questions