Skip to content

Instantly share code, notes, and snippets.

@greg84
Last active July 23, 2023 16:41
Show Gist options
  • Save greg84/0297569ef1052801a384aae9c75800cd to your computer and use it in GitHub Desktop.
Save greg84/0297569ef1052801a384aae9c75800cd to your computer and use it in GitHub Desktop.
MAUI KeyboardAwareContentView - Changes height of ContentView in iOS when keyboard overlaps it.
using System.Diagnostics;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using Platform = Microsoft.Maui.ApplicationModel.Platform;
#if IOS
using Foundation;
using Microsoft.Maui.Controls.PlatformConfiguration;
using UIKit;
#endif
namespace MyApp.Controls;
// workaround, adapted from: https://github.com/dotnet/maui/issues/10662#issuecomment-1386680526
public class KeyboardAwareContentView : ContentView
{
#if IOS
private NSObject _keyboardHeightChangeObserver;
private NSObject _keyboardHideObserver;
private NSObject _keyboardShowObserver;
public KeyboardAwareContentView()
{
RegisterForKeyboardNotifications();
}
~KeyboardAwareContentView()
{
UnregisterForKeyboardNotifications();
}
private bool _isShowCompleted;
private bool _isHideCompleted;
private void SetHeight(UIKeyboardEventArgs args)
{
nfloat top;
nfloat bottom;
try
{
var window = UIApplication.SharedApplication.Delegate.GetWindow();
top = window.SafeAreaInsets.Top;
bottom = window.SafeAreaInsets.Bottom;
}
catch
{
top = 0;
bottom = 0;
}
var result = (NSValue)args.Notification.UserInfo!.ObjectForKey(new NSString(UIKeyboard.FrameEndUserInfoKey));
var keyboardRect = result.CGRectValue;
try
{
var viewController = Platform.GetCurrentUIViewController();
if (viewController?.View != null)
{
// https://stackoverflow.com/questions/15402281/convert-uikeyboardframeenduserinfokey-to-view-or-window-coordinates#answer-16615391
var windowRect = viewController.View.ConvertRectFromView(result.CGRectValue, viewController.View.Window);
keyboardRect = viewController.View.ConvertRectFromView(windowRect, null);
}
}
catch
{
// ignored
}
ClearValue(HeightRequestProperty);
ClearValue(VerticalOptionsProperty);
var usingSafeArea = ((ContentPage)Parent).On<iOS>().UsingSafeArea();
var outerHeight = Shell.Current == null
? Height + bottom + (usingSafeArea ? top : 0)
: Shell.Current.Height;
var heightOffset = outerHeight - Height - (usingSafeArea ? top : 0);
HeightRequest = Height - keyboardRect.Height + heightOffset;
VerticalOptions = LayoutOptions.Start;
}
private void OnKeyboardHeightChanged(object sender, UIKeyboardEventArgs args)
{
if (_isShowCompleted && !_isHideCompleted)
{
try
{
SetHeight(args);
}
catch (Exception ex)
{
Debug.WriteLine($"KeyboardAwareContentView.OnKeyboardHeightChanged: {ex.Message} {ex.StackTrace}");
}
}
}
private void OnKeyboardHide(object sender, UIKeyboardEventArgs args)
{
_isHideCompleted = true;
_isShowCompleted = false;
if (IsSet(HeightRequestProperty))
{
ClearValue(HeightRequestProperty);
ClearValue(VerticalOptionsProperty);
}
}
private void OnKeyboardShow(object sender, UIKeyboardEventArgs args)
{
if (_isShowCompleted)
return;
try
{
SetHeight(args);
_isHideCompleted = false;
_isShowCompleted = true;
}
catch (Exception ex)
{
Debug.WriteLine($"KeyboardAwareContentView.OnKeyboardShow: {ex.Message} {ex.StackTrace}");
}
}
private void RegisterForKeyboardNotifications()
{
_keyboardShowObserver ??= UIKeyboard.Notifications.ObserveWillShow(OnKeyboardShow);
_keyboardHeightChangeObserver ??= UIKeyboard.Notifications.ObserveWillChangeFrame(OnKeyboardHeightChanged);
_keyboardHideObserver ??= UIKeyboard.Notifications.ObserveWillHide(OnKeyboardHide);
}
private void UnregisterForKeyboardNotifications()
{
if (_keyboardHeightChangeObserver != null)
{
_keyboardHeightChangeObserver.Dispose();
_keyboardHeightChangeObserver = null;
}
if (_keyboardHideObserver != null)
{
_keyboardHideObserver.Dispose();
_keyboardHideObserver = null;
}
if (_keyboardShowObserver != null)
{
_keyboardShowObserver.Dispose();
_keyboardShowObserver = null;
}
}
#endif
}
@greg84
Copy link
Author

greg84 commented Jul 23, 2023

To use this on a page, add an attribute to the ContentPage element:

xmlns:controls="clr-namespace:MyApp.Controls"

Then wrap the page content with this:

<controls:KeyboardAwareContentView>
</controls:KeyboardAwareContentView>

Ensure the page includes a ScrollView so content is scrollable when the keyboard is open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment