diff --git a/maui/src/Core/WindowOverlay/WindowOverlay.Android.cs b/maui/src/Core/WindowOverlay/WindowOverlay.Android.cs
index 1540a286..e97aa0a9 100644
--- a/maui/src/Core/WindowOverlay/WindowOverlay.Android.cs
+++ b/maui/src/Core/WindowOverlay/WindowOverlay.Android.cs
@@ -55,16 +55,24 @@ internal partial class SfWindowOverlay
///
internal virtual WindowOverlayStack CreateStack(Context context)
{
+ // Prefer creating the overlay stack via the MAUI handler so gesture/touch plumbing is wired.
WindowOverlayStack? windowOverlayStack = null;
+
if (_window is not null && _window.Handler is not null)
{
IMauiContext? mauiContext = _window.Handler.MauiContext;
if (mauiContext is not null)
{
- windowOverlayStack = (WindowOverlayStack?)_overlayStackView?.ToPlatform(mauiContext);
+ windowOverlayStack = _overlayStackView != null ? (WindowOverlayStack?)_overlayStackView.ToPlatform(mauiContext) : null;
}
}
+ // Fallback to HostMauiContext for native embedding (no MAUI Window).
+ if (windowOverlayStack == null && WindowOverlayHelper.HostMauiContext != null)
+ {
+ windowOverlayStack = _overlayStackView != null ? (WindowOverlayStack?)_overlayStackView.ToPlatform(WindowOverlayHelper.HostMauiContext) : null;
+ }
+
return windowOverlayStack is not null ? windowOverlayStack : new WindowOverlayStack(context);
}
@@ -251,6 +259,33 @@ internal bool AddToOverlay(MauiView childView)
}
}
}
+ else
+ {
+ // Native embedding path: no MAUI Window, use host Activity and IMauiContext set by the embedding app.
+ var activity = WindowOverlayHelper.HostActivity;
+ _density = WindowOverlayHelper._density;
+ _rootView = WindowOverlayHelper._platformRootView;
+ if (_rootView != null && activity != null)
+ {
+ _overlayStack = CreateStack(activity);
+ _windowManager = activity.WindowManager;
+ if (_overlayStack != null)
+ {
+ _overlayStack.LayoutChange += OnOverlayStackLayoutChange;
+ if (this._windowManagerLayoutParams == null)
+ {
+ this.GetWindowManagerLayoutParams();
+ }
+
+ // Prefer an existing native view if handler already realized.
+ if (WindowOverlayHelper.HostMauiContext != null)
+ {
+ _overlayContent = childView.ToPlatform(WindowOverlayHelper.HostMauiContext);
+ return true;
+ }
+ }
+ }
+ }
return false;
}
diff --git a/maui/src/Core/WindowOverlay/WindowOverlay.iOS.cs b/maui/src/Core/WindowOverlay/WindowOverlay.iOS.cs
index 9ad5c48d..edfd15f7 100644
--- a/maui/src/Core/WindowOverlay/WindowOverlay.iOS.cs
+++ b/maui/src/Core/WindowOverlay/WindowOverlay.iOS.cs
@@ -10,6 +10,10 @@ internal partial class SfWindowOverlay
{
#region Fields
+ internal static IMauiContext? MauiContext { get; set; }
+
+ internal static UIWindow? Window { get; set; }
+
UIView? _rootView;
WindowOverlayStack? _overlayStack;
UIView? _overlayContent;
@@ -23,23 +27,21 @@ internal partial class SfWindowOverlay
internal virtual WindowOverlayStack CreateStack()
{
WindowOverlayStack? windowOverlayStack = null;
- if (_window is not null && _window.Handler is not null)
+ IMauiContext? context = _window?.Handler?.MauiContext ?? SfWindowOverlay.MauiContext;
+ if (context is not null)
{
- IMauiContext? context = _window.Handler.MauiContext;
- if (context is not null)
- {
- windowOverlayStack = (WindowOverlayStack?)_overlayStackView?.ToPlatform(context);
+ windowOverlayStack = (WindowOverlayStack?)_overlayStackView?.ToPlatform(context);
- if (windowOverlayStack is not null && _overlayStackView is not null)
- {
- windowOverlayStack._canHandleTouch = !_overlayStackView.canHandleTouch;
- }
+ if (windowOverlayStack is not null && _overlayStackView is not null)
+ {
+ windowOverlayStack._canHandleTouch = !_overlayStackView.canHandleTouch;
}
}
- return windowOverlayStack is not null ? windowOverlayStack : new WindowOverlayStack();
+ return windowOverlayStack is not null ? windowOverlayStack : new WindowOverlayStack();
}
+
///
/// Adds or updates the child layout absolutely to the overlay stack.
///
@@ -171,6 +173,22 @@ internal void RemoveOverlay()
internal bool AddToOverlay(MauiView childView)
{
_window = WindowOverlayHelper._window;
+
+ // Native embedding path when MAUI Window is not yet available.
+ if ((_window == null || _window.Content == null) && SfWindowOverlay.MauiContext != null && SfWindowOverlay.Window != null)
+ {
+ _rootView = SfWindowOverlay.Window;
+ _overlayStack ??= CreateStack();
+ if (_overlayStack != null)
+ {
+ _overlayStack.AutoresizingMask = UIViewAutoresizing.All;
+ _overlayContent = childView.ToPlatform(SfWindowOverlay.MauiContext);
+ return true;
+ }
+
+ return false;
+ }
+
if (_window is not null && _window.Content is not null)
{
_rootView = WindowOverlayHelper._platformRootView;
@@ -260,7 +278,9 @@ internal void PositionOverlayContent(double x = 0, double y = 0)
keyWindow = UIApplication.SharedApplication.KeyWindow!;
}
- if (_rootView != keyWindow)
+ // Only switch to the keyWindow if we actually found one. For native embedding scenarios.
+ // the initial rootView may be provided via SfWindowOverlay.Window and keyWindow can be null.
+ if (keyWindow != null && _rootView != keyWindow)
{
_rootView = keyWindow;
}
diff --git a/maui/src/Core/WindowOverlay/WindowOverlayHelper.cs b/maui/src/Core/WindowOverlay/WindowOverlayHelper.cs
index 09efba2e..eea1ac34 100644
--- a/maui/src/Core/WindowOverlay/WindowOverlayHelper.cs
+++ b/maui/src/Core/WindowOverlay/WindowOverlayHelper.cs
@@ -39,7 +39,11 @@ internal static IWindow? _window
///
/// Gets the device density.
///
- internal static float _density => _window is not null ? _window.RequestDisplayDensity() : 1f;
+#if ANDROID
+ internal static float _density => _window != null ? _window.RequestDisplayDensity() : (HostActivity != null ? HostActivity.Resources?.DisplayMetrics?.Density ?? 1f : 1f);
+#else
+ internal static float _density => _window != null ? _window.RequestDisplayDensity() : 1f;
+#endif
///
/// Gets the root view of the device.
@@ -48,6 +52,16 @@ internal static IWindow? _window
#if ANDROID
+ ///
+ /// Optional host Activity for embedding scenarios where MAUI Window is not available.
+ ///
+ internal static Activity? HostActivity;
+
+ ///
+ /// Optional host IMauiContext for embedding scenarios when converting views without a Window.
+ ///
+ internal static IMauiContext? HostMauiContext;
+
///
/// Gets the decor view frame.
///
@@ -155,6 +169,20 @@ internal static IWindow? _window
}
#endif
}
+#if ANDROID
+ // Fallback for native embedding: derive root from host Activity when MAUI Window is unavailable.
+ else if (rootView == null && HostActivity != null)
+ {
+ try
+ {
+ rootView = HostActivity.FindViewById(Android.Resource.Id.Content) as ViewGroup;
+ }
+ catch
+ {
+ rootView = null;
+ }
+ }
+#endif
return rootView;
}
@@ -178,6 +206,17 @@ internal static IWindow? _window
return platformActivity.Window;
}
+ // Fallback for native embedding: use HostActivity if available.
+ else if (HostActivity != null)
+ {
+ if (HostActivity.WindowManager != null && HostActivity.WindowManager.DefaultDisplay != null)
+ {
+ return HostActivity.Window;
+ }
+
+ return null;
+ }
+
return null;
}
diff --git a/maui/src/Popup/Helpers/PopupExtension/PopupExtension.Android.cs b/maui/src/Popup/Helpers/PopupExtension/PopupExtension.Android.cs
index fea63751..348f8b14 100644
--- a/maui/src/Popup/Helpers/PopupExtension/PopupExtension.Android.cs
+++ b/maui/src/Popup/Helpers/PopupExtension/PopupExtension.Android.cs
@@ -474,7 +474,17 @@ internal static Rect GetRelativeViewBounds(this SfPopup popup, MauiView relative
{
int[] relativeViewOrigin = new int[2];
nativeRelativeView.GetLocationInWindow(relativeViewOrigin);
- relativeViewOrigin[1] = PopupExtension.GetLocationInApp(relativeView);
+
+ if (WindowOverlayHelper._decorViewContent is PlatformView decorView && nativeRelativeView.RootView == decorView)
+ {
+ relativeViewOrigin[1] = PopupExtension.GetLocationInApp(relativeView);
+ }
+ else
+ {
+ // 1008572: Popup relative position is incorrect when opening a popup within another popup on Android.
+ relativeViewOrigin[1] = (int)(relativeViewOrigin[1] / WindowOverlayHelper._density);
+ }
+
return new Rect(relativeViewOrigin[0] / WindowOverlayHelper._density, relativeViewOrigin[1], nativeRelativeView.Width / WindowOverlayHelper._density, nativeRelativeView.Height / WindowOverlayHelper._density);
}
else
diff --git a/maui/src/Popup/Helpers/PopupExtension/PopupExtension.iOS.cs b/maui/src/Popup/Helpers/PopupExtension/PopupExtension.iOS.cs
index e6360e13..1f716d05 100644
--- a/maui/src/Popup/Helpers/PopupExtension/PopupExtension.iOS.cs
+++ b/maui/src/Popup/Helpers/PopupExtension/PopupExtension.iOS.cs
@@ -22,7 +22,21 @@ internal static partial class PopupExtension
internal static int GetScreenWidth()
{
var rootView = WindowOverlayHelper._platformRootView;
- return rootView is not null ? (int)rootView.Frame.Width : 0;
+ if (rootView is not null)
+ {
+ return (int)rootView.Frame.Width;
+ }
+
+ // Native embedding: Get width from the underlying UIWindow.
+ var window = (WindowOverlayHelper._window?.ToPlatform() as UIWindow) ?? GetActiveWindow();
+ if (window is not null)
+ {
+ return (int)window.Bounds.Width;
+ }
+
+ // use UIScreen.MainScreen width (iOS-specific).
+ var screen = UIScreen.MainScreen;
+ return screen is not null ? (int)screen.Bounds.Width : 0;
}
///
@@ -32,7 +46,21 @@ internal static int GetScreenWidth()
internal static int GetScreenHeight()
{
var rootView = WindowOverlayHelper._platformRootView;
- return rootView is not null ? (int)rootView.Frame.Height : 0;
+ if (rootView is not null)
+ {
+ return (int)rootView.Frame.Height;
+ }
+
+ // Native embedding: Get height from the underlying UIWindow.
+ var window = (WindowOverlayHelper._window?.ToPlatform() as UIWindow) ?? GetActiveWindow();
+ if (window is not null)
+ {
+ return (int)window.Bounds.Height;
+ }
+
+ // use UIScreen.MainScreen height (iOS-specific).
+ var screen = UIScreen.MainScreen;
+ return screen is not null ? (int)screen.Bounds.Height : 0;
}
///
@@ -81,6 +109,43 @@ internal static int GetY(this MauiView view)
/// Returns the status bar height.
internal static double GetStatusBarHeight()
{
+ // Try to obtain the active platform window (supports native embedding scenarios).
+ var platformWindow = (WindowOverlayHelper._window?.ToPlatform() as UIWindow) ?? GetActiveWindow();
+
+ if (platformWindow is not null && WindowOverlayHelper._window is null)
+ {
+ try
+ {
+ // For iOS 13+ with scenes, use the StatusBarManager when available.
+ if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
+ {
+ var scene = platformWindow.WindowScene;
+ if (scene is not null)
+ {
+#if IOS || MACCATALYST
+ var statusBarManager = scene.StatusBarManager;
+ if (statusBarManager is not null)
+ {
+ return statusBarManager.StatusBarFrame.Height;
+ }
+#endif
+ }
+ }
+
+ // Fallback to the top safe area inset if present.
+ if (platformWindow.SafeAreaInsets.Top > 0)
+ {
+ return platformWindow.SafeAreaInsets.Top;
+ }
+ }
+ catch
+ {
+ // ignore and return 0 as final fallback.
+ }
+
+ return 0;
+ }
+
return 0;
}
@@ -90,7 +155,7 @@ internal static double GetStatusBarHeight()
/// Returns action bar height.
internal static int GetActionBarHeight()
{
- var platformWindow = WindowOverlayHelper._window?.ToPlatform() as UIWindow;
+ var platformWindow = (WindowOverlayHelper._window?.ToPlatform() as UIWindow) ?? GetActiveWindow();
// To calculate the navigationBar height with the page Y position.
if (GetMainPage() is Page page && page.Handler is not null && page.Handler.PlatformView is UIView pageNativeView && platformWindow is not null)
@@ -120,53 +185,138 @@ internal static int GetActionBarHeight()
internal static int GetSafeAreaHeight(string position)
{
// The popup is drawn within the safe area when shown relative to a view. Safe area calculations apply only to the iOS X simulator on iOS.
-#pragma warning disable CS0618 // Suppressing CS0618 warning because Page.GetUseSafeArea is marked obsolete in .NET 10.
- if (Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page.GetUseSafeArea(GetMainPage()))
-#pragma warning restore CS0618
- {
- var platformWindow = WindowOverlayHelper._window?.ToPlatform() as UIWindow;
- UIInterfaceOrientation? statusBarOrientation = null;
- var scene = platformWindow?.WindowScene;
- if (scene != null)
+ if (WindowOverlayHelper._window != null)
+ {
+ // PopUp is drawn in safe area also crap when it is shown based on relative to view point.
+ // Fix Details: SafeArea is calculated only for iOS X simulator in iOS.
+#if NET10_0
+ if (GetSafeAreaEdges())
+#else
+ if (Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page.GetUseSafeArea(GetMainPage()))
+#endif
{
-#if IOS || MACCATALYST
- if (OperatingSystem.IsIOSVersionAtLeast(16, 0) || OperatingSystem.IsMacCatalystVersionAtLeast(16, 0))
- {
- statusBarOrientation = scene.EffectiveGeometry.InterfaceOrientation;
- }
- else
+ var platformWindow = WindowOverlayHelper._window?.ToPlatform() as UIWindow;
+ UIInterfaceOrientation? statusBarOrientation = null;
+ var scene = platformWindow?.WindowScene;
+ if (scene != null)
{
+#if IOS || MACCATALYST
+ if (OperatingSystem.IsIOSVersionAtLeast(16, 0) || OperatingSystem.IsMacCatalystVersionAtLeast(16, 0))
+ {
+ statusBarOrientation = scene.EffectiveGeometry.InterfaceOrientation;
+ }
+ else
+ {
#pragma warning disable CA1422 // InterfaceOrientation is obsoleted on newer SDKs; guarded by OS version checks
- statusBarOrientation = scene.InterfaceOrientation;
+ statusBarOrientation = scene.InterfaceOrientation;
#pragma warning restore CA1422
- }
+ }
#endif
- }
+ }
- // Since StatusBarHeight is already calculated, the top safe area does not need to be computed.
- if (position == "Top")
- {
- return platformWindow is not null ? (int)platformWindow.SafeAreaInsets.Top : 0;
- }
- else if ((statusBarOrientation == UIInterfaceOrientation.Portrait || statusBarOrientation == UIInterfaceOrientation.PortraitUpsideDown || statusBarOrientation == UIInterfaceOrientation.LandscapeLeft || statusBarOrientation == UIInterfaceOrientation.LandscapeRight || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceUp || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceDown) && position == "Bottom")
- {
- return platformWindow?.SafeAreaInsets.Bottom > 0 ? (int)platformWindow.SafeAreaInsets.Bottom : 0;
- }
- else if ((UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceUp || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceDown) && position == "Right" && GetScreenWidth() > GetScreenHeight())
- {
- return platformWindow?.SafeAreaInsets.Right > 0 ? (int)platformWindow.SafeAreaInsets.Right : 0;
- }
- else if ((UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceUp || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceDown) && (position == "Left") && GetScreenWidth() > GetScreenHeight())
- {
- return platformWindow?.SafeAreaInsets.Left > 0 ? (int)platformWindow.SafeAreaInsets.Left : 0;
- }
- else if (statusBarOrientation == UIInterfaceOrientation.LandscapeLeft)
- {
- return platformWindow?.SafeAreaInsets.Left > 0 ? (int)platformWindow.SafeAreaInsets.Left : 0;
+ // Since StatusBarHeight is already calculated, the top safe area does not need to be computed.
+ if (position == "Top")
+ {
+ return platformWindow is not null ? (int)platformWindow.SafeAreaInsets.Top : 0;
+ }
+ else if ((statusBarOrientation == UIInterfaceOrientation.Portrait || statusBarOrientation == UIInterfaceOrientation.PortraitUpsideDown || statusBarOrientation == UIInterfaceOrientation.LandscapeLeft || statusBarOrientation == UIInterfaceOrientation.LandscapeRight || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceUp || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceDown) && position == "Bottom")
+ {
+ return platformWindow?.SafeAreaInsets.Bottom > 0 ? (int)platformWindow.SafeAreaInsets.Bottom : 0;
+ }
+ else if ((UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceUp || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceDown) && position == "Right" && GetScreenWidth() > GetScreenHeight())
+ {
+ return platformWindow?.SafeAreaInsets.Right > 0 ? (int)platformWindow.SafeAreaInsets.Right : 0;
+ }
+ else if ((UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceUp || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.FaceDown) && (position == "Left") && GetScreenWidth() > GetScreenHeight())
+ {
+ return platformWindow?.SafeAreaInsets.Left > 0 ? (int)platformWindow.SafeAreaInsets.Left : 0;
+ }
+ else if (statusBarOrientation == UIInterfaceOrientation.LandscapeLeft)
+ {
+ return platformWindow?.SafeAreaInsets.Left > 0 ? (int)platformWindow.SafeAreaInsets.Left : 0;
+ }
+ else if (statusBarOrientation == UIInterfaceOrientation.LandscapeRight)
+ {
+ return platformWindow?.SafeAreaInsets.Right > 0 ? (int)platformWindow.SafeAreaInsets.Right : 0;
+ }
}
- else if (statusBarOrientation == UIInterfaceOrientation.LandscapeRight)
+ }
+ else
+ {
+ if (GetSafeAreaEdges())
{
- return platformWindow?.SafeAreaInsets.Right > 0 ? (int)platformWindow.SafeAreaInsets.Right : 0;
+ // Prefer the platform root view's safe area in native embedding, then the platform window.
+ var rootView = WindowOverlayHelper._platformRootView as UIView
+ ?? WindowOverlayHelper._window?.ToPlatform() as UIView
+ ?? PopupExtension.GetActiveWindow() as UIView;
+
+ var platformWindow = (WindowOverlayHelper._window?.ToPlatform() as UIWindow) ?? GetActiveWindow();
+
+ UIInterfaceOrientation? statusBarOrientation = null;
+ var scene = platformWindow?.WindowScene;
+ if (scene != null)
+ {
+#if IOS || MACCATALYST
+ if (OperatingSystem.IsIOSVersionAtLeast(16, 0) || OperatingSystem.IsMacCatalystVersionAtLeast(16, 0))
+ {
+ statusBarOrientation = scene.EffectiveGeometry.InterfaceOrientation;
+ }
+ else
+ {
+#pragma warning disable CA1422 // InterfaceOrientation is obsoleted on newer SDKs; guarded by OS version checks.
+ statusBarOrientation = scene.InterfaceOrientation;
+#pragma warning restore CA1422
+ }
+#endif
+ }
+
+ // Top: prefer rootView inset, then window inset, then status bar height fallback.
+ if (position == "Top")
+ {
+ if (rootView != null && rootView.SafeAreaInsets.Top > 0)
+ {
+ return (int)rootView.SafeAreaInsets.Top;
+ }
+
+ if (platformWindow != null && platformWindow.SafeAreaInsets.Top > 0)
+ {
+ return (int)platformWindow.SafeAreaInsets.Top;
+ }
+
+ return (int)GetStatusBarHeight();
+ }
+
+ // Bottom: prefer rootView inset then window inset.
+ if (position == "Bottom")
+ {
+ if (rootView != null && rootView.SafeAreaInsets.Bottom > 0)
+ {
+ return (int)rootView.SafeAreaInsets.Bottom;
+ }
+
+ return platformWindow?.SafeAreaInsets.Bottom > 0 ? (int)platformWindow.SafeAreaInsets.Bottom : 0;
+ }
+
+ // Left/Right: consider device orientation and prefer rootView then window.
+ if (position == "Left")
+ {
+ if (rootView != null && rootView.SafeAreaInsets.Left > 0)
+ {
+ return (int)rootView.SafeAreaInsets.Left;
+ }
+
+ return platformWindow?.SafeAreaInsets.Left > 0 ? (int)platformWindow.SafeAreaInsets.Left : 0;
+ }
+
+ if (position == "Right")
+ {
+ if (rootView != null && rootView.SafeAreaInsets.Right > 0)
+ {
+ return (int)rootView.SafeAreaInsets.Right;
+ }
+
+ return platformWindow?.SafeAreaInsets.Right > 0 ? (int)platformWindow.SafeAreaInsets.Right : 0;
+ }
}
}
@@ -250,9 +400,12 @@ internal static Rect GetRelativeViewBounds(this SfPopup popup, MauiView relative
if (relativeView.Handler != null && relativeView.Handler.PlatformView != null && relativeView.Handler.PlatformView is PlatformView nativeRelativeView
&& WindowOverlayHelper._platformRootView != null)
{
- PlatformView rootView = WindowOverlayHelper._platformRootView;
- nfloat[] location = new nfloat[2];
- CGPoint relativeViewOrigin = nativeRelativeView.ConvertPointToView(new CGPoint(0, 0), rootView);
+ PlatformView? rootView = WindowOverlayHelper._platformRootView as UIView
+ ?? WindowOverlayHelper._window?.ToPlatform() as UIView
+ ?? PopupExtension.GetActiveWindow() as UIView;
+ CGPoint relativeViewOrigin = rootView != null
+ ? nativeRelativeView.ConvertPointToView(new CGPoint(0, 0), rootView)
+ : nativeRelativeView.ConvertPointToView(new CGPoint(0, 0), null);
return new Rect(relativeViewOrigin.X, relativeViewOrigin.Y, nativeRelativeView.Frame.Width, nativeRelativeView.Frame.Height);
}
else
@@ -261,6 +414,88 @@ internal static Rect GetRelativeViewBounds(this SfPopup popup, MauiView relative
}
}
+ ///
+ /// This method will returns the SafeAreEdges of the Content page.
+ ///
+ /// Returns the whether the SafeAreaEdges is set for the page.
+ internal static bool GetSafeAreaEdges()
+ {
+ if (WindowOverlayHelper._window != null)
+ {
+#if NET10_0
+ if (GetMainPage() != null && GetMainPage() is ContentPage page && page.SafeAreaEdges != SafeAreaEdges.None)
+ {
+ return true;
+ }
+#endif
+ }
+ else
+ {
+ var rootView = WindowOverlayHelper._platformRootView;
+ if (rootView != null)
+ {
+ if (rootView.SafeAreaInsets.Top > 0 || rootView.SafeAreaInsets.Bottom > 0 || rootView.SafeAreaInsets.Left > 0 || rootView.SafeAreaInsets.Right > 0)
+ {
+ return true;
+ }
+ }
+
+ var platformWindow = (WindowOverlayHelper._window?.ToPlatform() as UIWindow) ?? GetActiveWindow();
+ if (platformWindow != null)
+ {
+ if (platformWindow.SafeAreaInsets.Top > 0 || platformWindow.SafeAreaInsets.Bottom > 0 || platformWindow.SafeAreaInsets.Left > 0 || platformWindow.SafeAreaInsets.Right > 0)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Attempts to get the active UIWindow for the application, including iOS 13+ multi-scene setups.
+ ///
+ /// The active UIWindow if available; otherwise null.
+ internal static UIWindow? GetActiveWindow()
+ {
+ try
+ {
+ if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
+ {
+ var app = UIApplication.SharedApplication;
+ foreach (var scene in app.ConnectedScenes)
+ {
+ UIWindow? keyWindow = null;
+ if (scene is UIWindowScene windowScene)
+ {
+ keyWindow = windowScene.Windows?.FirstOrDefault(w => w.IsKeyWindow);
+ if (keyWindow is not null)
+ {
+ return keyWindow;
+ }
+
+ keyWindow = windowScene.Windows?.FirstOrDefault();
+ if (keyWindow is not null)
+ {
+ return keyWindow;
+ }
+ }
+ }
+ }
+
+#pragma warning disable CA1422 // Fallback for pre-iOS 13 or if scenes are not available.
+ return UIApplication.SharedApplication?.KeyWindow
+ ?? UIApplication.SharedApplication?.Windows?.FirstOrDefault(w => w.IsKeyWindow)
+ ?? UIApplication.SharedApplication?.Windows?.FirstOrDefault();
+#pragma warning disable
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
#endregion
#region Private Methods
diff --git a/maui/src/Popup/PopupFooter.cs b/maui/src/Popup/PopupFooter.cs
index f0bb340e..a435d177 100644
--- a/maui/src/Popup/PopupFooter.cs
+++ b/maui/src/Popup/PopupFooter.cs
@@ -73,6 +73,14 @@ internal class PopupFooter : SfView
///
public PopupFooter()
{
+#if IOS
+ // 916944 : When setting SafeArea for a page, the value does not update within the popup content due to a framework issue.
+#if NET10_0
+ this.IgnoreSafeArea = !PopupExtension.GetSafeAreaEdges();
+#else
+ this.IgnoreSafeArea = !Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page.GetUseSafeArea(PopupExtension.GetMainPage());
+#endif
+#endif
}
///
diff --git a/maui/src/Popup/PopupHeader.cs b/maui/src/Popup/PopupHeader.cs
index 1f518879..7ac90d70 100644
--- a/maui/src/Popup/PopupHeader.cs
+++ b/maui/src/Popup/PopupHeader.cs
@@ -57,6 +57,14 @@ internal class PopupHeader : SfView
///
public PopupHeader()
{
+#if IOS
+ // 895700 : When Page SafeArea is false, close icon overlaps header.Because HeaderView arranging with safeArea.
+#if NET10_0
+ this.IgnoreSafeArea = !PopupExtension.GetSafeAreaEdges();
+#else
+ this.IgnoreSafeArea = !Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page.GetUseSafeArea(PopupExtension.GetMainPage());
+#endif
+#endif
Initialize();
}
diff --git a/maui/src/Popup/PopupMessageView.cs b/maui/src/Popup/PopupMessageView.cs
index 3e8a54f7..565edc1d 100644
--- a/maui/src/Popup/PopupMessageView.cs
+++ b/maui/src/Popup/PopupMessageView.cs
@@ -50,6 +50,14 @@ internal class PopupMessageView : SfContentView
///
public PopupMessageView()
{
+#if IOS
+ // 916944 : When setting SafeArea for a page, the value does not update within the popup content due to a framework issue.
+#if NET10_0
+ this.IgnoreSafeArea = !PopupExtension.GetSafeAreaEdges();
+#else
+ this.IgnoreSafeArea = !Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.Page.GetUseSafeArea(PopupExtension.GetMainPage());
+#endif
+#endif
}
///
diff --git a/maui/src/Popup/SfPopup/SfPopup.Android.cs b/maui/src/Popup/SfPopup/SfPopup.Android.cs
index 4726d391..392cb17b 100644
--- a/maui/src/Popup/SfPopup/SfPopup.Android.cs
+++ b/maui/src/Popup/SfPopup/SfPopup.Android.cs
@@ -45,6 +45,26 @@ public partial class SfPopup
#endregion
+ #region Constructor
+
+ ///
+ /// Initializes a new instance of the class for native Android embedding scenarios where a MAUI Window is not available.
+ ///
+ /// The host that provides the necessary Android context when embedding the popup in a native application.
+ /// The host that provides access to MAUI-specific services when embedding the SfPopup in a native Android scenario.
+ public SfPopup(Activity activity, IMauiContext mauiContext)
+ : this()
+ {
+ // Register the host Activity and optional IMauiContext for native embedding.
+ Syncfusion.Maui.Toolkit.Internals.WindowOverlayHelper.HostActivity = activity;
+ if (mauiContext != null)
+ {
+ Syncfusion.Maui.Toolkit.Internals.WindowOverlayHelper.HostMauiContext = mauiContext;
+ }
+ }
+
+ #endregion
+
#region Internal Methods
///
@@ -64,6 +84,21 @@ internal void WirePlatformSpecificEvents()
}
}
}
+ else if (WindowOverlayHelper._window == null)
+ {
+ // Native Embedding: MainPage is null. Use the platform root or decor view to wire GlobalLayout.
+ // WindowOverlayHelper is initialized via SfPopup(Activity, IMauiContext) constructor in embedding scenarios.
+ var nativeView = WindowOverlayHelper._platformRootView as PlatformView
+ ?? WindowOverlayHelper._decorViewContent as PlatformView;
+
+ if (nativeView != null && nativeView.ViewTreeObserver != null)
+ {
+ nativeView.ViewTreeObserver.GlobalLayout += this.OnViewTreeObserverGlobalLayout;
+ }
+
+ // In native embedding, handle orientation and display changes by listening to .
+ DeviceDisplay.MainDisplayInfoChanged += this.OnViewTreeObserverGlobalLayout;
+ }
}
///
@@ -83,6 +118,19 @@ internal void UnWirePlatformSpecificEvents()
}
}
}
+ else if (WindowOverlayHelper._window == null)
+ {
+ var nativeView = WindowOverlayHelper._platformRootView as PlatformView
+ ?? WindowOverlayHelper._decorViewContent as PlatformView;
+
+ if (nativeView != null && nativeView.ViewTreeObserver != null)
+ {
+ nativeView.ViewTreeObserver.GlobalLayout -= this.OnViewTreeObserverGlobalLayout;
+ }
+
+ // Unwire orientation/display change handler.
+ DeviceDisplay.MainDisplayInfoChanged -= this.OnViewTreeObserverGlobalLayout;
+ }
}
///
@@ -90,7 +138,7 @@ internal void UnWirePlatformSpecificEvents()
///
internal void PopupPositionBasedOnKeyboard()
{
- if (_popupView is not null)
+ if (this._popupView is not null)
{
_popupView.HandlerChanged += OnPopupViewHandlerChanged;
}
diff --git a/maui/src/Popup/SfPopup/SfPopup.iOS.cs b/maui/src/Popup/SfPopup/SfPopup.iOS.cs
index aa9c65eb..f45c3e17 100644
--- a/maui/src/Popup/SfPopup/SfPopup.iOS.cs
+++ b/maui/src/Popup/SfPopup/SfPopup.iOS.cs
@@ -3,6 +3,7 @@
using Foundation;
using Microsoft.Maui.Controls.Platform;
using Microsoft.Maui.Platform;
+using Syncfusion.Maui.Toolkit.Internals;
using UIKit;
namespace Syncfusion.Maui.Toolkit.Popup
@@ -45,6 +46,24 @@ internal Page? ModalPage
#endregion
+ #region Constructor
+
+ ///
+ /// Initializes a new instance of the class for native iOS embedding scenarios where a MAUI Window is not available.
+ ///
+ /// The external that hosts the popup when no MAUI Window exists.
+ /// The providing access to MAUI services in native iOS embedding.
+ public SfPopup(UIWindow window, IMauiContext mauiContext)
+ : this()
+ {
+ Syncfusion.Maui.Toolkit.Internals.SfWindowOverlay.MauiContext = mauiContext;
+ Syncfusion.Maui.Toolkit.Internals.SfWindowOverlay.Window = window;
+
+ // Try initializing overlay again with external host available.
+ this.InitializeOverlay();
+ }
+ #endregion
+
#region Internal Methods
///
@@ -67,7 +86,10 @@ internal void WirePlatformSpecificEvents()
}
else
{
- windowPage.SizeChanged += OnMainPageSizeChanged;
+ windowPage.SizeChanged += this.OnMainPageSizeChanged;
+
+ // In native embedding, handle orientation and display changes by listening to .
+ DeviceDisplay.MainDisplayInfoChanged += this.OnMainPageSizeChanged;
}
}
@@ -94,7 +116,10 @@ internal void UnWirePlatformSpecificEvents()
}
else
{
- windowPage.SizeChanged -= OnMainPageSizeChanged;
+ windowPage.SizeChanged -= this.OnMainPageSizeChanged;
+
+ // Unwire orientation/display change handler.
+ DeviceDisplay.MainDisplayInfoChanged -= this.OnMainPageSizeChanged;
}
}
@@ -206,7 +231,7 @@ void OnMainPageSizeChanged(object? sender, EventArgs e)
SyncPopupDimensionFields();
// While show the popup using ShowRelativeToView, Popup is not positioned properly when resize the window in MAC and Split the screen in iOS.
- if (_relativeView is not null)
+ if (_relativeView is not null || this.RelativeView is not null)
{
Dispatcher.Dispatch(ResetPopupWidthHeight);
}