Skip to content

Commit db28e2e

Browse files
Add support for iOS/Mac specific modals styled as popovers (#23984)
* Add support for iOS Specific popover-style modals * Cleanup PR * Update PublicAPI for other platforms * Update PublicAPI.Unshipped.txt * Minor fixes * Update iOSModalPagePresentationStyle.xaml.cs --------- Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com>
1 parent 2a78b81 commit db28e2e

File tree

16 files changed

+294
-133
lines changed

16 files changed

+294
-133
lines changed

src/Controls/docs/Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific/UIModalPresentationStyle.xml

-105
This file was deleted.

src/Controls/samples/Controls.Sample/Pages/PlatformSpecifics/iOS/iOSModalPagePresentationStyle.xaml

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
55
xmlns:ios="clr-namespace:Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;assembly=Microsoft.Maui.Controls"
66
x:Class="Maui.Controls.Sample.Pages.iOSModalPagePresentationStyle"
7-
Title="Modal FormSheet Page"
7+
Title="Modal Presentation Style Page"
88
ios:Page.ModalPresentationStyle="OverFullScreen">
99
<StackLayout Margin="20,35,20,20">
10-
<Label Text="Modal popup as a form sheet." HorizontalOptions="Center" />
10+
<Label Text="Modal popup as a FormSheet or as a Popover" HorizontalOptions="Center" />
11+
<Button Text="Push modal with UIModalPresentationStyle.FormSheet" Clicked="OnPushFormSheetClicked" />
12+
<Button x:Name="originButton" Text="Push modal with UIModalPresentationStyle.Popover" Clicked="OnPushPopoverClicked" />
13+
<Button x:Name="originButton2" Text="Push modal with UIModalPresentationStyle.Popover and offset" Clicked="OnPushPopoverOffsetClicked" />
1114
<Button Text="Return to Platform-Specifics List" Clicked="OnReturnButtonClicked" />
1215
</StackLayout>
1316
</ContentPage>
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,72 @@
11
using System;
2+
using System.Drawing;
23
using Microsoft.Maui.Controls;
4+
using Microsoft.Maui.Controls.PlatformConfiguration;
5+
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
36

47
namespace Maui.Controls.Sample.Pages
58
{
69
public partial class iOSModalPagePresentationStyle : ContentPage
710
{
11+
bool isChildPage;
12+
13+
public Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.UIModalPresentationStyle presentationStyle =
14+
Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific.UIModalPresentationStyle.Automatic;
15+
816
public iOSModalPagePresentationStyle()
917
{
1018
InitializeComponent();
1119
}
1220

21+
public iOSModalPagePresentationStyle(UIModalPresentationStyle presentationStyle, bool isChildPage = true,
22+
View? modalOrigin = null, Rectangle? rectangle = null)
23+
{
24+
InitializeComponent();
25+
26+
if(modalOrigin is not null)
27+
{
28+
On<iOS>().SetModalPopoverView(modalOrigin);
29+
}
30+
31+
if(rectangle is not null)
32+
{
33+
On<iOS>().SetModalPopoverRect(rectangle.Value);
34+
}
35+
36+
On<iOS>().SetModalPresentationStyle(presentationStyle);
37+
38+
this.isChildPage = isChildPage;
39+
}
40+
41+
async void OnPushFormSheetClicked(object sender, EventArgs e)
42+
{
43+
Microsoft.Maui.Controls.Page pushMe = new iOSModalPagePresentationStyle(UIModalPresentationStyle.FormSheet, true);
44+
await Navigation.PushModalAsync(pushMe);
45+
}
46+
47+
async void OnPushPopoverClicked(object sender, EventArgs e)
48+
{
49+
Microsoft.Maui.Controls.Page pushMe = new iOSModalPagePresentationStyle(UIModalPresentationStyle.Popover, true, originButton);
50+
await Navigation.PushModalAsync(pushMe);
51+
}
52+
53+
async void OnPushPopoverOffsetClicked(object sender, EventArgs e)
54+
{
55+
var offset = new System.Drawing.Rectangle(0, 0, 100, 10);
56+
Microsoft.Maui.Controls.Page pushMe = new iOSModalPagePresentationStyle(UIModalPresentationStyle.Popover, true, originButton2, offset);
57+
await Navigation.PushModalAsync(pushMe);
58+
}
59+
1360
async void OnReturnButtonClicked(object sender, EventArgs e)
1461
{
15-
await Navigation.PopModalAsync();
62+
if (isChildPage)
63+
{
64+
await Navigation.PopModalAsync();
65+
}
66+
else
67+
{
68+
await Navigation.PopAsync();
69+
}
1670
}
1771
}
1872
}

src/Controls/samples/Controls.Sample/ViewModels/PlatformSpecificsViewModel.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ protected override IEnumerable<SectionModel> CreateItems()
7575
new SectionModel(typeof(iOSListViewWithCellPage), "ListView/Cell Platform-Specifics",
7676
"This iOS platform-specific controls whether ListView header cells float during scrolling."),
7777

78-
new SectionModel(typeof(iOSModalPagePresentationStyle), "Modal FormSheet Page",
78+
new SectionModel(typeof(iOSModalPagePresentationStyle), "Modal Presentation Style Page",
7979
"This iOS platform-specific is used to set the presentation style of a modal page, and in addition can be used to display modal pages that have transparent backgrounds."),
8080

8181
new SectionModel(typeof(iOSNavigationPage), "NavigationPage Platform-Specifics",

src/Controls/src/Core/Platform/ModalNavigationManager/ModalNavigationManager.iOS.cs

+23-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ partial void InitializePlatform()
3232
void OnWindowPropertyChanging(object sender, PropertyChangingEventArgs e)
3333
{
3434
if (e.PropertyName != Window.PageProperty.PropertyName)
35+
{
3536
return;
37+
}
3638

3739
if (_currentPage is not null &&
3840
_currentPage.Handler is IPlatformViewHandler pvh &&
@@ -108,7 +110,9 @@ Task PushModalPlatformAsync(Page modal, bool animated)
108110
_platformModalPages.Add(modal);
109111

110112
if (_window?.Page?.Handler is not null)
113+
{
111114
return PresentModal(modal, animated && _window.IsActivated);
115+
}
112116

113117
return Task.CompletedTask;
114118
}
@@ -142,8 +146,23 @@ async Task PresentModal(Page modal, bool animated)
142146

143147
if (WindowViewController is not null)
144148
{
145-
await WindowViewController.PresentViewControllerAsync(wrapper, animated);
146-
await Task.Delay(5);
149+
// This is branched, because if the modal is a popover and can't display correctly for some reason, we want it to fail
150+
if (wrapper.ModalPresentationStyle == UIKit.UIModalPresentationStyle.Popover){
151+
if(wrapper.PopoverPresentationController is not null && WindowViewController.View is not null)
152+
{
153+
await WindowViewController.PresentViewControllerAsync(wrapper, animated);
154+
await Task.Delay(5);
155+
}
156+
else
157+
{
158+
failed = true;
159+
}
160+
}
161+
else
162+
{
163+
await WindowViewController.PresentViewControllerAsync(wrapper, animated);
164+
await Task.Delay(5);
165+
}
147166
}
148167
}
149168
catch
@@ -157,7 +176,9 @@ async Task PresentModal(Page modal, bool animated)
157176
presentFinished.SetResult();
158177

159178
if (!failed)
179+
{
160180
SyncModalStackWhenPlatformIsReady();
181+
}
161182
}
162183

163184
}

src/Controls/src/Core/Platform/iOS/ControlsModalWrapper.cs

+29-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.ComponentModel;
4-
using System.Linq;
54
using Foundation;
65
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
76
using Microsoft.Maui.Graphics;
@@ -19,25 +18,40 @@ internal ControlsModalWrapper(IPlatformViewHandler modal)
1918
{
2019
_modal = modal;
2120

22-
if (_modal.VirtualView is IElementConfiguration<Page> elementConfiguration &&
23-
elementConfiguration.On<PlatformConfiguration.iOS>()?.ModalPresentationStyle() is PlatformConfiguration.iOSSpecific.UIModalPresentationStyle style)
21+
if (_modal.VirtualView is IElementConfiguration<Page> elementConfiguration)
2422
{
25-
var result = style.ToPlatformModalPresentationStyle();
26-
27-
if (!(OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsTvOSVersionAtLeast(13)) && result == UIKit.UIModalPresentationStyle.Automatic)
23+
if (elementConfiguration.On<PlatformConfiguration.iOS>()?.ModalPresentationStyle() is PlatformConfiguration.iOSSpecific.UIModalPresentationStyle style)
2824
{
29-
result = UIKit.UIModalPresentationStyle.FullScreen;
30-
}
25+
var result = style.ToPlatformModalPresentationStyle();
3126

32-
if (result == UIKit.UIModalPresentationStyle.FullScreen)
33-
{
34-
Color modalBkgndColor = ((Page)_modal.VirtualView).BackgroundColor;
27+
if (!(OperatingSystem.IsIOSVersionAtLeast(13) || OperatingSystem.IsTvOSVersionAtLeast(13)) && result == UIKit.UIModalPresentationStyle.Automatic)
28+
{
29+
result = UIKit.UIModalPresentationStyle.FullScreen;
30+
}
3531

36-
if (modalBkgndColor?.Alpha < 1)
37-
result = UIKit.UIModalPresentationStyle.OverFullScreen;
38-
}
32+
if (result == UIKit.UIModalPresentationStyle.FullScreen)
33+
{
34+
Color modalBkgndColor = ((Page)_modal.VirtualView).BackgroundColor;
3935

40-
ModalPresentationStyle = result;
36+
if (modalBkgndColor?.Alpha < 1)
37+
result = UIKit.UIModalPresentationStyle.OverFullScreen;
38+
}
39+
ModalPresentationStyle = result;
40+
}
41+
if (PopoverPresentationController != null)
42+
{
43+
if (elementConfiguration.On<PlatformConfiguration.iOS>()?.ModalPopoverSourceView() is View popoverSourceView)
44+
{
45+
PopoverPresentationController.SourceView = popoverSourceView.ToPlatform();
46+
}
47+
if (elementConfiguration.On<PlatformConfiguration.iOS>()?.ModalPopoverRect() is System.Drawing.Rectangle rect)
48+
{
49+
if (!rect.IsEmpty)
50+
{
51+
PopoverPresentationController.SourceRect = new CoreGraphics.CGRect(rect.X, rect.Y, rect.Width, rect.Height);
52+
}
53+
}
54+
}
4155
}
4256

4357
UpdateBackgroundColor();

src/Controls/src/Core/Platform/iOS/Extensions/Extensions.cs

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public static UIModalPresentationStyle ToPlatformModalPresentationStyle(this Pla
2424
return UIModalPresentationStyle.OverFullScreen;
2525
case PlatformConfiguration.iOSSpecific.UIModalPresentationStyle.PageSheet:
2626
return UIModalPresentationStyle.PageSheet;
27+
case PlatformConfiguration.iOSSpecific.UIModalPresentationStyle.Popover:
28+
return UIModalPresentationStyle.Popover;
2729
default:
2830
throw new ArgumentOutOfRangeException(nameof(style));
2931
}

0 commit comments

Comments
 (0)