Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

ModalInPresentation flag in iOS not working #12301

Open
btschumy opened this issue Sep 29, 2020 · 2 comments
Open

ModalInPresentation flag in iOS not working #12301

btschumy opened this issue Sep 29, 2020 · 2 comments

Comments

@btschumy
Copy link

Description

Setting the ModalInPresentation flag for a UIViewController should, according to Apple:

The default value of this property is NO. When you set it to YES, UIKit ignores events outside the view controller's bounds and prevents the interactive dismissal of the view controller while it is onscreen.

However, this flag does not seem to work in Xamarin Forms (all versions I've tried). If you set it YES, you can still dismiss the modal page by dragging it to the bottom of the screen. If the flag is set NO, you should be able to dismiss the page by tapping outside the pages boundary but this seems to fail as well.

I've created an example app at www.otherwise.com/ModalPresentation.zip that illustrates the problems. The example app uses a custom PageRenderer to set the ModalInPresentation flag in multiple places (since it wasn't really clear where it needs to be set).

Note, I've been told that this flag is working correctly in a native Xamarin iOS app. It seems to only fail in Xamarin Forms.

Note that this bug may be related to another problem I've reported in issue #12300. This refers to a crash when tapping outside the modal page and then dismissing the page.

Steps to Reproduce

  1. Download example app at www.otherwise.com/ModalPresentation.zip
  2. Run the app on an iPad (simulator or real device).
  3. Tap the Show Modal Panel button to display the modal panel. This panel is displayed using:
Navigation.PushModalAsync(new OTNavigationPage(new ModalPage()));
  1. The code as downloaded sets ModalInPresentation to YES, however you can still dismiss the panel by dragging down.

Expected Behavior

The panel should not be able to be dismissed by dragging down with this flag set to true.

Actual Behavior

It can be dismissed by dragging.

Basic Information

  • Version with issue:
    All versions of the Xamarin Forms package that I've tried (3.6.709228 to 4.8.0.1364)
  • Last known good version:
    None
  • IDE:
    Jetbrains Rider
  • Platform Target Frameworks:
    • iOS: iOS 11+
  • Nuget Packages:
  • Affected Devices:
    Observed on iPads running iOS 13 and 14

Screenshots

Reproduction Link

www.otherwise.com/ModalPresentation.zip

Workaround

None that I've found

@btschumy btschumy added s/unverified New report that has yet to be verified t/bug 🐛 labels Sep 29, 2020
@hartez hartez added a/modal p/iOS 🍎 e/5 🕔 5 and removed s/unverified New report that has yet to be verified labels Sep 30, 2020
@jeffington
Copy link

jeffington commented Mar 2, 2021

@btschumy Hi Bill, I have been having the same problem as you. Here's my work-around. I created an extension that walks up the UIViewController hierarchy and sets the ModalInPresentation property for every UIViewController. I call this extension from the ViewDidAppear method of a custom NavigationRenderer.

NOTE: It's possible that you can call this from other places in the ViewController lifecycle, but I have not tested that and I found that the ViewController didn't get wrapped in a ModalWrapper until pretty late in the lifecycle.

    public static class UIViewController_Extensions
    {

        public static void SetModalInPresentation(this UIViewController viewController, bool modalInPresentation)
        {

            viewController.ModalInPresentation = modalInPresentation;
            if (viewController.ParentViewController != null)
            {
                viewController.ParentViewController.SetModalInPresentation(modalInPresentation);
            }

        }

    }

I was able to find a work-around because of your observation that the ModalInPresentation property worked on Xamarin.iOS but not on Xamarin.Forms. It seems like the issue is that modal ViewControllers are wrapped in a class called ModalWrapper and it's possible that ModalInPresentation is not being bubbled up through the ViewControllers. I read that you can set ModalInPresentation on any modal page in a navigation stack for the modal behavior to be updated, and it's possible that there is a disconnect where the navigation stack is not structured how the iOS system is expecting.

Thanks for sharing your observations and I hope this helps!

@aspeckt-112
Copy link

aspeckt-112 commented May 7, 2023

Hello from 2023!

I came across this problem in .NET MAUI. I've created a Frakenstien's monster of a fix using a suggestion here: dotnet/maui#7174

In the iOS folder, I created the following class:

using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using ContentView = Microsoft.Maui.Platform.ContentView;

namespace Corkboard.Mobile;

// https://github.com/dotnet/maui/issues/7174
public partial class AuthenticationPageHandler : PageHandler
{
    protected override ContentView CreatePlatformView()
    {
        _ = VirtualView ??
            throw new InvalidOperationException($"{nameof(VirtualView)} must be set to create a LayoutView");
        _ = MauiContext ?? throw new InvalidOperationException($"{nameof(MauiContext)} cannot be null");

        if (ViewController == null)
            ViewController = new AuthenticationPageHandlerViewController(VirtualView, MauiContext);

        if (ViewController is PageViewController pc && pc.CurrentPlatformView is ContentView pv)
            return pv;

        if (ViewController.View is Microsoft.Maui.Platform.ContentView cv)
            return cv;

        throw new InvalidOperationException(
            $"PageViewController.View must be a {nameof(Microsoft.Maui.Platform.ContentView)}");
    }
}

Also in the iOS folder, added the following class (thanks to @jeffington above for the extension method):

using Microsoft.Maui.Platform;
using UIKit;

namespace Corkboard.Mobile;

public class AuthenticationPageHandlerViewController : PageViewController
{
    public AuthenticationPageHandlerViewController(IView page, IMauiContext mauiContext) : base(page, mauiContext)
    {
    }

    public override void ViewDidAppear(bool animated)
    {
        base.ViewDidAppear(animated);
        this.SetModalInPresentation(true);
    }
}

public static class UIViewControllerExtensions
{
    public static void SetModalInPresentation(this UIViewController viewController, bool modalInPresentation)
    {
        // TODO Do I care if it's less than iOS 13?
        viewController.ModalInPresentation = modalInPresentation;
        if (viewController.ParentViewController != null)
        {
            viewController.ParentViewController.SetModalInPresentation(modalInPresentation);
        }
    }
}

And in my MauiProgram.cs:

        builder.ConfigureMauiHandlers(collection =>
        {
#if IOS
            collection.AddHandler(typeof(AuthenticationPage), typeof(AuthenticationPageHandler));
#endif
        });

I feel quite dirty. I'm not sure if there's a better way, but it does work.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants