Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions samples/CommunityToolkit.Maui.Sample/MauiProgram.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ static void RegisterViewsAndViewModels(in IServiceCollection services)
services.AddTransientPopup<TransparentPopup>();
services.AddTransientPopup<UpdatingPopup, UpdatingPopupViewModel>();
services.AddTransientPopup<XamlBindingPopup, XamlBindingPopupViewModel>();
services.AddTransientPopup<SoftInputKeyboardPopup>();
}

static void RegisterEssentials(in IServiceCollection services)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ScrollView>
<VerticalStackLayout Spacing="12">
<Button Text="Simple Popup" Clicked="HandleSimplePopupButtonClicked" />

<Button Text="Self Closing Popup" Clicked="HandleSelfClosingPopupButtonClicked"/>

<Button Text="Button Popup" Clicked="HandleButtonPopupButtonClicked" />
Expand All @@ -35,7 +35,7 @@
<Button Text="Updating Popup" Command="{Binding UpdatingPopupCommand}" />

<Button Text="Show Popup content" Command="{Binding ShowPopupContentCommand}" />

<Button Text="Show Popup in a Modal Page in a Custom Navigation Page" Clicked="HandleModalPopupInCustomNavigationPage" />

<Button Text="Custom Positioning Popup" Clicked="HandlePopupPositionButtonClicked" />
Expand All @@ -49,11 +49,13 @@
<Button Text="Popup Style Page" Clicked="HandleStylePopupButtonClicked" />

<Button Text="OnDisappearing Popup" Clicked="HandleOnDisappearingPopupClicked" />

<Button Text="Complex Popup" Clicked="HandleComplexPopupClicked" />

<Button Text="Collection View Popup" Clicked="HandleCollectionViewPopupClicked" />


<Button Text="Software Input Keyboard Popup" Clicked="HandleSoftInputKeyboardPopupClicked" />

</VerticalStackLayout>
</ScrollView>
</ContentPage.Content>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,9 @@ async void HandleModalPopupInCustomNavigationPage(object? sender, EventArgs even
var customNavigationPage = new NavigationPage(modalPopupPage);
await Shell.Current.Navigation.PushModalAsync(customNavigationPage, true);
}

async void HandleSoftInputKeyboardPopupClicked(object? sender, EventArgs eventArgs)
{
await popupService.ShowPopupAsync<SoftInputKeyboardPopup>(Navigation);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8" ?>
<mct:Popup xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:mct="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="CommunityToolkit.Maui.Sample.Views.Popups.SoftInputKeyboardPopup"
CanBeDismissedByTappingOutsideOfPopup="True">

<VerticalStackLayout Spacing="12">

<Label
Text="Keyboard for Entry"
FontSize="18"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center"
FontAttributes="Bold" />

<Entry
Placeholder="Click here to enter text..."
TextColor="Black"
HorizontalTextAlignment="Start"
VerticalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center" />

<Label
x:Name="PickerForIOSLabel"
Text="Keyboard for Picker (iOS only)"
FontSize="18"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center"
FontAttributes="Bold"
IsVisible="False" />

<Picker
x:Name="PickerForIOS"
Title="Select a monkey"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center"
IsVisible="False">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Baboon</x:String>
<x:String>Capuchin Monkey</x:String>
<x:String>Blue Monkey</x:String>
<x:String>Squirrel Monkey</x:String>
<x:String>Golden Lion Tamarin</x:String>
<x:String>Howler Monkey</x:String>
<x:String>Japanese Macaque</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>

<Label
x:Name="DatePickerForIOSLabel"
Text="Keyboard for DatePicker (iOS only)"
FontSize="18"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center"
FontAttributes="Bold"
IsVisible="False" />

<DatePicker
x:Name="DatePickerForIOS"
MinimumDate="01/01/2020"
MaximumDate="12/31/2030"
Date="02/23/2026"
VerticalOptions="Center"
HorizontalOptions="Center"
IsVisible="False" />

<Button
Text="Close"
HorizontalOptions="Center"
VerticalOptions="Center"
Clicked="ClosePopup" />

</VerticalStackLayout>
</mct:Popup>
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using CommunityToolkit.Maui.Views;

namespace CommunityToolkit.Maui.Sample.Views.Popups;

public partial class SoftInputKeyboardPopup : Popup
{
public SoftInputKeyboardPopup()
{
InitializeComponent();

#if IOS
PickerForIOSLabel.IsVisible = true;
PickerForIOS.IsVisible = true;
DatePickerForIOSLabel.IsVisible = true;
DatePickerForIOS.IsVisible = true;
#endif
}

async void ClosePopup(object? sender, EventArgs eventArgs)
{
await CloseAsync();
}
}
95 changes: 94 additions & 1 deletion src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@

using CommunityToolkit.Maui.Extensions;
#if ANDROID
using Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific;
#elif IOS && !NET10_0_OR_GREATER

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can have back compatibility, this version will not install on NET 9 anyway. On toolkit we only support the current .NET version

using Foundation;
using UIKit;
#endif

namespace CommunityToolkit.Maui.Views;

Expand Down Expand Up @@ -56,7 +62,7 @@ public Popup()
/// </remarks>
[BindableProperty]
public partial bool CanBeDismissedByTappingOutsideOfPopup { get; set; }

/// <summary>
/// Gets or sets the padding between the <see cref="Popup"/> border and the <see cref="Popup"/> content.
/// </summary>
Expand All @@ -71,14 +77,65 @@ public Popup()
/// </summary>
public virtual Task CloseAsync(CancellationToken token = default) => GetPopupPage().CloseAsync(new PopupResult(false), token);

#if IOS && !NET10_0_OR_GREATER
/// <summary>
/// Stores the keyboard will show notification observer to manage keyboard lifecycle.
/// </summary>
NSObject? willShow;

/// <summary>
/// Stores the keyboard will hide notification observer to manage keyboard lifecycle.
/// </summary>
NSObject? willHide;

/// <summary>
/// Stores the native platform view to adjust safe area insets when keyboard appears.
/// </summary>
UIView? popupNativeView;

/// <summary>
/// Stores the view controller associated with the popup to adjust safe area insets.
/// </summary>
UIViewController? viewController;
#endif

internal void NotifyPopupIsOpened()
{
Opened?.Invoke(this, EventArgs.Empty);

#if ANDROID

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of using #ifs can you create a platform file (Popup.android.cs) and put the code there? You can define partial void methods to implement and called from the shared layer.

// On Android, configure the window soft input mode to resize when the keyboard appears
Microsoft.Maui.Controls.Application.Current?.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
#elif IOS && !NET10_0_OR_GREATER
// On iOS, store the native view and subscribe to keyboard events to adjust safe area insets
if (Handler?.PlatformView is UIView view)
{
popupNativeView = view;
}

willShow = UIKeyboard.Notifications.ObserveWillShow((_, args) => HandleKeyboard(args));

willHide = UIKeyboard.Notifications.ObserveWillHide((_, args) => ResetSafeArea());
#endif
}

internal void NotifyPopupIsClosed()
{
Closed?.Invoke(this, EventArgs.Empty);

#if ANDROID
// On Android, reset the window soft input mode to unspecified when the popup closes
Microsoft.Maui.Controls.Application.Current?.On<Microsoft.Maui.Controls.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Unspecified);
#elif IOS && !NET10_0_OR_GREATER
// On iOS, dispose of keyboard event observers and clean up stored references
willShow?.Dispose();
willHide?.Dispose();

willShow = willHide = null;

popupNativeView = null;
viewController = null;
#endif
}

private protected PopupPage GetPopupPage()
Expand All @@ -97,6 +154,42 @@ private protected PopupPage GetPopupPage()

throw new PopupNotFoundException();
}

#if IOS && !NET10_0_OR_GREATER
/// <summary>
/// Adjusts the safe area insets when the keyboard appears on iOS.
/// </summary>
/// <param name="args">The keyboard event arguments containing the keyboard frame.</param>
void HandleKeyboard(UIKeyboardEventArgs args)
{
if (popupNativeView is null)
{
return;
}

// Get the view controller associated with the popup's native view
viewController ??= popupNativeView.Window?.RootViewController?.PresentedViewController;

if (viewController is null)
{
return;
}

// Adjust the bottom safe area inset to account for keyboard height
viewController.AdditionalSafeAreaInsets = new UIEdgeInsets(0, 0, args.FrameEnd.Height, 0);
}

/// <summary>
/// Resets the safe area insets when the keyboard is hidden on iOS.
/// </summary>
void ResetSafeArea()
{
if (viewController is not null)
{
viewController.AdditionalSafeAreaInsets = UIEdgeInsets.Zero;
}
}
#endif
}

/// <summary>
Expand Down
9 changes: 7 additions & 2 deletions src/CommunityToolkit.Maui/Views/Popup/PopupPage.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@ public PopupPage(Popup popup, IPopupOptions? popupOptions)
Shell.SetPresentationMode(this, PresentationMode.ModalNotAnimated);
On<iOS>().SetModalPresentationStyle(UIModalPresentationStyle.OverFullScreen);
NavigationPage.SetHasNavigationBar(this, false);

#if NET10_0_OR_GREATER

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can remove this

// On .NET 10.0 and greater, configure safe area edges to respect the keyboard and system UI while keeping the popup content within safe boundaries
this.SafeAreaEdges = new SafeAreaEdges(SafeAreaRegions.Container, SafeAreaRegions.Container, SafeAreaRegions.Container, SafeAreaRegions.SoftInput);
#endif
}

public event EventHandler<IPopupResult>? PopupClosed;
Expand Down Expand Up @@ -221,7 +226,7 @@ public PopupPageLayout(in Popup popupContent, in IPopupOptions options, in Actio
Content = popupContent
};

// Bind `Popup` values through to Border using OneWay Bindings
// Bind `Popup` values through to Border using OneWay Bindings
PopupBorder.SetBinding(Border.MarginProperty, static (Popup popup) => popup.Margin, source: popupContent, mode: BindingMode.OneWay, converter: new MarginConverter());
PopupBorder.SetBinding(Border.BackgroundProperty, static (Popup popup) => popup.Background, source: popupContent, mode: BindingMode.OneWay);
PopupBorder.SetBinding(Border.BackgroundColorProperty, static (Popup popup) => popup.BackgroundColor, source: popupContent, mode: BindingMode.OneWay, converter: new BackgroundColorConverter());
Expand Down Expand Up @@ -254,7 +259,7 @@ void HandleOverlayTapped(object? sender, TappedEventArgs e)
return;
}

// Execute tapOutsideOfPopupCommand only if tap occurred outside the PopupBorder
// Execute tapOutsideOfPopupCommand only if tap occurred outside the PopupBorder
if (PopupBorder.Bounds.Contains(position.Value) is false)
{
tryExecuteTapOutsideOfPopupCommand();
Expand Down
Loading