File: Platform\ModalNavigationManager\ModalNavigationManager.iOS.cs
Web Access
Project: src\src\Controls\src\Core\Controls.Core.csproj (Microsoft.Maui.Controls)
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Maui.Controls.Internals;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Microsoft.Maui.Platform;
using ObjCRuntime;
using UIKit;
 
namespace Microsoft.Maui.Controls.Platform
{
	internal partial class ModalNavigationManager
	{
		// We need to wait for the window to be activated the first time before
		// we push any modal views.
		// After it's been activated we can push modals if the app has been sent to
		// the background so we don't care if it becomes inactive
		bool _platformActivated;
		bool _waitForModalToFinish;
 
		partial void InitializePlatform()
		{
			_window.Activated += OnWindowActivated;
			_window.Resumed += (_, _) => SyncPlatformModalStack();
			_window.HandlerChanging += OnPlatformWindowHandlerChanging;
			_window.Destroying += (_, _) => _platformActivated = false;
			_window.PropertyChanging += OnWindowPropertyChanging;
			_platformActivated = _window.IsActivated;
		}
 
		void OnWindowPropertyChanging(object sender, PropertyChangingEventArgs e)
		{
			if (e.PropertyName != Window.PageProperty.PropertyName)
				return;
 
			if (_currentPage is not null &&
				_currentPage.Handler is IPlatformViewHandler pvh &&
				pvh.ViewController?.PresentedViewController is ModalWrapper &&
				_window.Page != _currentPage)
			{
				ClearModalPages(xplat: true, platform: true);
 
				// Dismissing the root modal will dismiss everything
				pvh.ViewController.DismissViewController(false, null);
			}
		}
 
		Task SyncModalStackWhenPlatformIsReadyAsync() =>
			SyncPlatformModalStackAsync();
 
		bool IsModalPlatformReady => _platformActivated && !_waitForModalToFinish;
 
		void OnPlatformWindowHandlerChanging(object? sender, HandlerChangingEventArgs e)
		{
			_platformActivated = _window.IsActivated;
		}
 
		void OnWindowActivated(object? sender, EventArgs e)
		{
			if (!_platformActivated)
			{
				_platformActivated = true;
				SyncModalStackWhenPlatformIsReady();
			}
		}
 
		UIViewController? WindowViewController
		{
			get
			{
				if (_window?.Page?.Handler is IPlatformViewHandler pvh &&
					pvh.ViewController?.ViewIfLoaded?.Window is not null)
				{
					return pvh.ViewController;
				}
 
				return WindowMauiContext.
						GetPlatformWindow()?
						.RootViewController;
			}
		}
 
		async Task<Page> PopModalPlatformAsync(bool animated)
		{
			var modal = CurrentPlatformModalPage;
			_platformModalPages.Remove(modal);
 
			var controller = (modal.Handler as IPlatformViewHandler)?.ViewController;
 
			if (controller?.ParentViewController is ModalWrapper modalWrapper &&
				modalWrapper.PresentingViewController is not null)
			{
				await modalWrapper.PresentingViewController.DismissViewControllerAsync(animated);
				return modal;
			}
 
			// if the presenting VC is null that means the modal window was already dismissed
			// this will usually happen as a result of swapping out the content on the window
			// which is what was acting as the PresentingViewController
			return modal;
		}
 
		Task PushModalPlatformAsync(Page modal, bool animated)
		{
			EndEditing();
 
			_platformModalPages.Add(modal);
 
			if (_window?.Page?.Handler is not null)
				return PresentModal(modal, animated && _window.IsActivated);
 
			return Task.CompletedTask;
		}
 
		async Task PresentModal(Page modal, bool animated)
		{
			bool failed = false;
			TaskCompletionSource presentFinished = new TaskCompletionSource();
			try
			{
				_waitForModalToFinishTask = presentFinished.Task;
				_waitForModalToFinish = true;
 
				var wrapper = new ControlsModalWrapper(modal.ToHandler(WindowMauiContext));
 
				if (_platformModalPages.Count > 1)
				{
					var topPage = _platformModalPages[_platformModalPages.Count - 2];
					var controller = (topPage?.Handler as IPlatformViewHandler)?.ViewController;
					if (controller is not null)
					{
						await controller.PresentViewControllerAsync(wrapper, animated);
						await Task.Delay(5);
						return;
					}
				}
 
				// One might wonder why these delays are here... well thats a great question. It turns out iOS will claim the 
				// presentation is complete before it really is. It does not however inform you when it is really done (and thus 
				// would be safe to dismiss the VC). Fortunately this is almost never an issue
 
				if (WindowViewController is not null)
				{
					await WindowViewController.PresentViewControllerAsync(wrapper, animated);
					await Task.Delay(5);
				}
			}
			catch
			{
				failed = true;
				throw;
			}
			finally
			{
				_waitForModalToFinish = false;
				presentFinished.SetResult();
 
				if (!failed)
					SyncModalStackWhenPlatformIsReady();
			}
 
		}
 
		void EndEditing()
		{
			// If any text entry controls have focus, we need to end their editing session
			// so that they are not the first responder; if we don't some things (like the activity indicator
			// on pull-to-refresh) will not work correctly. 
 
			// The topmost modal on the stack will have the Window; we can use that to end any current
			// editing that's going on 
			if (_platformModalPages.Count > 0)
			{
				var uiViewController = (_platformModalPages[_platformModalPages.Count - 1].Handler as IPlatformViewHandler)?.ViewController;
				uiViewController?.View?.Window?.EndEditing(true);
				return;
			}
 
			// TODO MAUI
			// If there aren't any modals, then the platform renderer will have the Window
			_window?.NativeWindow?.EndEditing(true);
		}
	}
}