How to save the keyboard from covering my user interface rather than resizing it?

On iOS, Xamarin.Forms resizes the screen when the keyboard appears, when the root of the node is ScrollView . But when the root of the node is not a ScrollView , the keyboard hides part of the user interface. How do you go on with this?

+9
ios xamarin.forms
source share
4 answers

A way to fix this is with a special render that listens for the keyboard to appear and adds padding while it is there.

In the PCL project KeyboardResizingAwareContentPage.cs :

 using Xamarin.Forms; public class KeyboardResizingAwareContentPage : ContentPage { public bool CancelsTouchesInView = true; } 

In your iOS project IosKeyboardFixPageRenderer.cs :

 using Foundation; using MyProject.iOS.Renderers; using UIKit; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; [assembly: ExportRenderer(typeof(KeyboardResizingAwareContentPage), typeof(IosKeyboardFixPageRenderer))] namespace MyProject.iOS.Renderers { public class IosKeyboardFixPageRenderer : PageRenderer { NSObject observerHideKeyboard; NSObject observerShowKeyboard; public override void ViewDidLoad() { base.ViewDidLoad(); var cp = Element as KeyboardResizingAwareContentPage; if (cp != null && !cp.CancelsTouchesInView) { foreach (var g in View.GestureRecognizers) { g.CancelsTouchesInView = false; } } } public override void ViewWillAppear(bool animated) { base.ViewWillAppear(animated); observerHideKeyboard = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillHideNotification, OnKeyboardNotification); observerShowKeyboard = NSNotificationCenter.DefaultCenter.AddObserver(UIKeyboard.WillShowNotification, OnKeyboardNotification); } public override void ViewWillDisappear(bool animated) { base.ViewWillDisappear(animated); NSNotificationCenter.DefaultCenter.RemoveObserver(observerHideKeyboard); NSNotificationCenter.DefaultCenter.RemoveObserver(observerShowKeyboard); } void OnKeyboardNotification(NSNotification notification) { if (!IsViewLoaded) return; var frameBegin = UIKeyboard.FrameBeginFromNotification(notification); var frameEnd = UIKeyboard.FrameEndFromNotification(notification); var page = Element as ContentPage; if (page != null && !(page.Content is ScrollView)) { var padding = page.Padding; page.Padding = new Thickness(padding.Left, padding.Top, padding.Right, padding.Bottom + frameBegin.Top - frameEnd.Top); } } } } 
+17
source share

I found that KeyboardOverlap plugin works better than Anthony's solution.

Here is how I use it:

  • Create your own renderer
 public class KeyboardResizingAwareContentPage : ContentPage { } 
  1. Implement your own renderer in iOS. Here is the important part of the paulpatarinski code:
 [Preserve (AllMembers = true)] public class KeyboardOverlapRenderer : PageRenderer { NSObject _keyboardShowObserver; NSObject _keyboardHideObserver; private bool _pageWasShiftedUp; private double _activeViewBottom; private bool _isKeyboardShown; public static void Init () { var now = DateTime.Now; Debug.WriteLine ("Keyboard Overlap plugin initialized {0}", now); } public override void ViewWillAppear (bool animated) { base.ViewWillAppear (animated); var page = Element as ContentPage; if (page != null) { var contentScrollView = page.Content as ScrollView; if (contentScrollView != null) return; RegisterForKeyboardNotifications (); } } public override void ViewWillDisappear (bool animated) { base.ViewWillDisappear (animated); UnregisterForKeyboardNotifications (); } void RegisterForKeyboardNotifications () { if (_keyboardShowObserver == null) _keyboardShowObserver = NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillShowNotification, OnKeyboardShow); if (_keyboardHideObserver == null) _keyboardHideObserver = NSNotificationCenter.DefaultCenter.AddObserver (UIKeyboard.WillHideNotification, OnKeyboardHide); } void UnregisterForKeyboardNotifications () { _isKeyboardShown = false; if (_keyboardShowObserver != null) { NSNotificationCenter.DefaultCenter.RemoveObserver (_keyboardShowObserver); _keyboardShowObserver.Dispose (); _keyboardShowObserver = null; } if (_keyboardHideObserver != null) { NSNotificationCenter.DefaultCenter.RemoveObserver (_keyboardHideObserver); _keyboardHideObserver.Dispose (); _keyboardHideObserver = null; } } protected virtual void OnKeyboardShow (NSNotification notification) { if (!IsViewLoaded || _isKeyboardShown) return; _isKeyboardShown = true; var activeView = View.FindFirstResponder (); if (activeView == null) return; var keyboardFrame = UIKeyboard.FrameEndFromNotification (notification); var isOverlapping = activeView.IsKeyboardOverlapping (View, keyboardFrame); if (!isOverlapping) return; if (isOverlapping) { _activeViewBottom = activeView.GetViewRelativeBottom (View); ShiftPageUp (keyboardFrame.Height, _activeViewBottom); } } private void OnKeyboardHide (NSNotification notification) { if (!IsViewLoaded) return; _isKeyboardShown = false; var keyboardFrame = UIKeyboard.FrameEndFromNotification (notification); if (_pageWasShiftedUp) { ShiftPageDown (keyboardFrame.Height, _activeViewBottom); } } private void ShiftPageUp (nfloat keyboardHeight, double activeViewBottom) { var pageFrame = Element.Bounds; var newY = pageFrame.Y + CalculateShiftByAmount (pageFrame.Height, keyboardHeight, activeViewBottom); Element.LayoutTo (new Rectangle (pageFrame.X, newY, pageFrame.Width, pageFrame.Height)); _pageWasShiftedUp = true; } private void ShiftPageDown (nfloat keyboardHeight, double activeViewBottom) { var pageFrame = Element.Bounds; var newY = pageFrame.Y - CalculateShiftByAmount (pageFrame.Height, keyboardHeight, activeViewBottom); Element.LayoutTo (new Rectangle (pageFrame.X, newY, pageFrame.Width, pageFrame.Height)); _pageWasShiftedUp = false; } private double CalculateShiftByAmount (double pageHeight, nfloat keyboardHeight, double activeViewBottom) { return (pageHeight - activeViewBottom) - keyboardHeight; } } 

And the missing extensions:

 public static class ViewExtensions { /// <summary> /// Find the first responder in the <paramref name="view"/> subview hierarchy /// </summary> /// <param name="view"> /// A <see cref="UIView"/> /// </param> /// <returns> /// A <see cref="UIView"/> that is the first responder or null if there is no first responder /// </returns> public static UIView FindFirstResponder (this UIView view) { if (view.IsFirstResponder) { return view; } foreach (UIView subView in view.Subviews) { var firstResponder = subView.FindFirstResponder (); if (firstResponder != null) return firstResponder; } return null; } /// <summary> /// Returns the new view Bottom (Y + Height) coordinates relative to the rootView /// </summary> /// <returns>The view relative bottom.</returns> /// <param name="view">View.</param> /// <param name="rootView">Root view.</param> public static double GetViewRelativeBottom (this UIView view, UIView rootView) { var viewRelativeCoordinates = rootView.ConvertPointFromView (view.Frame.Location, view); var activeViewRoundedY = Math.Round (viewRelativeCoordinates.Y, 2); return activeViewRoundedY + view.Frame.Height; } /// <summary> /// Determines if the UIView is overlapped by the keyboard /// </summary> /// <returns><c>true</c> if is keyboard overlapping the specified activeView rootView keyboardFrame; otherwise, <c>false</c>.</returns> /// <param name="activeView">Active view.</param> /// <param name="rootView">Root view.</param> /// <param name="keyboardFrame">Keyboard frame.</param> public static bool IsKeyboardOverlapping (this UIView activeView, UIView rootView, CGRect keyboardFrame) { var activeViewBottom = activeView.GetViewRelativeBottom (rootView); var pageHeight = rootView.Frame.Height; var keyboardHeight = keyboardFrame.Height; var isOverlapping = activeViewBottom >= (pageHeight - keyboardHeight); return isOverlapping; } } 
  1. Use custom page rendering
 public partial class LoginPage : KeyboardResizingAwareContentPage { public LoginPage() { // your content // note: you have to use base.Navigation.PushAsync(), base.DisplayAlert(), ... } } 
 <?xml version="1.0" encoding="utf-8" ?> <renderer:KeyboardResizingAwareContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="App.Pages.LoginPage" xmlns:renderer="clr-namespace:App.CustomRenderers;assembly=App"> <!-- your content --> </renderer:KeyboardResizingAwareContentPage> 

All loans for this belong to Paul! Thanks for this!

+3
source share

This Xamarin forum question discusses it.

Also, if you want to use it in Android using Xamarin / Forms, you can just set this in your main action:

 [Activity(WindowSoftInputMode = Android.Views.SoftInput.AdjustResize)] public class MainActivity ... 
+2
source share

For Android, add the following code to MainActivity after LoadApplication(new App());

 App.Current.On<Xamarin.Forms.PlatformConfiguration.Android>(). UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize); 
0
source share

All Articles