Skip to content

Unable to use UIAdaptivePresentationControllerDelegate events for modal pages #20473

Description

@Axemasta

Description

If I show a modal page on iOS with ModalPresentationStyle set to PageSheet, or any presentation where the page can be dismissed via a gesture, there is no event to hook into to know that page has been dismissed at the maui level (as far as I am aware).

This leads to a situation where frameworks like Prism don't detect page transitions and can get stuck. MVVMCross had this issue in Xamarin Forms and fixed it here. I have tried porting this code to my maui app and the UIAdaptivePresentationControllerDelegate dismiss method is never fired.

I have tried setting it in 2 ways, 1 inheriting the NavigationHandler and setting the PresentationController delegate:

public class DismissAwareNavigationPageHandler : NavigationRenderer
{
    public override void ViewDidLoad()
    {
        Action dismissAction = () =>
        {
            Debug.WriteLine("Presentation controller did dismiss (UINavigationController)");
        };

        if (PresentationController != null)
        {
            Debug.WriteLine("Adding dismiss aware presentation delegate to navigation page");
            PresentationController.Delegate = new DismissAwareUIPresentationControllerDelegate(dismissAction);
        }
        
        base.ViewDidLoad();
    }
}

Another way I tried was using a content page renderer and setting a view controller which would set this delegate:

public class DismissAwarePopoverPageHandler : PageHandler
{
    class PopoverPageViewController(IView page, IMauiContext mauiContext) : PageViewController(page, mauiContext)
    {
        public override void ViewDidLoad()
        {
            Debug.WriteLine("Handler view controller connected");
            
            if (PresentationController != null)
            {
                Action dismissAction = () =>
                {
                    Debug.WriteLine("Presentation controller did dismiss (UIViewController)");
                };
                
                Debug.WriteLine("Attaching UIAdaptivePresentationControllerDelegate to parent navigation controller");
                PresentationController.Delegate = new DismissAwareUIPresentationControllerDelegate(dismissAction);
            }
            
            base.ViewDidLoad();
        }

        public override void WillMoveToParentViewController(UIViewController? parent)
        {
            base.WillMoveToParentViewController(parent);
            
            if (parent is NavigationRenderer && parent?.NavigationController?.PresentationController != null)
            {
                Action dismissAction = () =>
                {
                    Debug.WriteLine("Presentation controller did dismiss (UIViewController parent UINavigationController)");
                };
                
                Debug.WriteLine("Attaching UIAdaptivePresentationControllerDelegate to parent navigation controller via child uiviewcontroller");
                parent.NavigationController.PresentationController.Delegate = new DismissAwareUIPresentationControllerDelegate(dismissAction);
            }
            
        }
    }

    protected override Microsoft.Maui.Platform.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 PopoverPageViewController(VirtualView, MauiContext);

        if (ViewController is PageViewController pc && pc.CurrentPlatformView is Microsoft.Maui.Platform.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)}");
    }
}

In my reproduction I provide a native swift example where the code to observe the dismiss events is super easy:

class ModalViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        if (navigationController != nil) {
            self.navigationController?.presentationController?.delegate = self
        }
        else {
            self.presentationController?.delegate = self
        }
    }
}

extension ModalViewController : UIAdaptivePresentationControllerDelegate {
 
    func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
        print("Modal was dismissed")
    }
}

I am looking to implement this functionality in maui, since its not working I wonder if it is an issue with the .NET ios sdk, otherwise is there a better way to set this delegate where it would actually work.

Steps to Reproduce

  • Display a modal content page
  • Attached a handler to set the UIAdaptivePresentationControllerDelegate for the associated UIViewController
  • Perform dismiss action on the presented page
  • Expected: dismiss event fires
  • Actual: no event fires

Link to public reproduction project repository

https://github.com/Axemasta/DismissPresentationControllerRepro

Version with bug

8.0.6

Is this a regression from previous behavior?

Yes, this used to work in Xamarin.Forms

Last version that worked well

Unknown/Other

Affected platforms

iOS

Affected platform versions

iOS 13+

Did you find any workaround?

No

Relevant log output

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-controls-modalmigration-compatibilityXamarin.Forms to .NET MAUI Migration, Upgrade Assistant, Try-Convertplatform/ioss/triagedIssue has been revieweds/verifiedVerified / Reproducible Issue ready for Engineering Triaget/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions