Xamarin.Forms.Plugins icon indicating copy to clipboard operation
Xamarin.Forms.Plugins copied to clipboard

KeyboardOverlap with Editor

Open MichaelRumpler opened this issue 8 years ago • 10 comments

I have an Editor with Height="300" near the top of a page. When the user taps the first row, then the whole Editor is moved up as far as the keyboard is high. Unfortunately the edited text is then not visible anymore.

This can easily be reproduced in the SampleApp if you remove the StackLayout from KeyboardOverlap\SampleApp\SampleApp\Pages\MultiLineText.xaml. Then the Editor occupies the whole page. On my iPad the bottom half of the Editor is empty. When I tap anywhere on the text, then the text is shifted up and I only see the empty lower half of the Editor above the keyboard.

You should make sure, that the cursor always stays visible. If possible, then the height of the UITextView should be changed to be not more than the visible area above the keyboard. That way scrolling within the Editor should still be possible.

MichaelRumpler avatar Oct 04 '16 16:10 MichaelRumpler

I run into the same problem. Is there any solution available for this problem?

L4rS6 avatar Dec 12 '16 20:12 L4rS6

I have the sample problem, hope there is a solution for it.

cd155 avatar Feb 02 '17 19:02 cd155

me too! :(

phamthanhtong avatar Mar 23 '17 07:03 phamthanhtong

@MichaelRumpler, @cd155 : I fixed it. In file Path:

KeyboardOverlap/KeyboardOverlap/KeyboardOverlap.Forms.Plugin.iOSUnified/Extensions/ViewExtensions.cs

Change this code: Before: var viewRelativeCoordinates = rootView.ConvertPointFromView(view.Frame.Location, view); After: var viewRelativeCoordinates = rootView.ConvertPointFromView(view.Bounds.Location, view); Hope help you!

phamthanhtong avatar Mar 24 '17 04:03 phamthanhtong

@phamthanhtong I use nuget package, do you know how i suppose change it with nuget package?

cd155 avatar Mar 24 '17 13:03 cd155

@phamthanhtong

Did you do anything else? I have to find a way to limit how much the page will shift up, based on distance between the top of the Editor and the top of the page.

LuigiMaestrelli avatar Mar 24 '17 13:03 LuigiMaestrelli

@cd155 I don't use nugget package. Instead of installing it, I only need to add 2 files to my xamarin.iOS project and easy to customize it:

  1. https://github.com/paulpatarinski/Xamarin.Forms.Plugins/blob/master/KeyboardOverlap/KeyboardOverlap/KeyboardOverlap.Forms.Plugin.iOSUnified/KeyboardOverlapRenderer.cs
  2. https://github.com/paulpatarinski/Xamarin.Forms.Plugins/blob/master/KeyboardOverlap/KeyboardOverlap/KeyboardOverlap.Forms.Plugin.iOSUnified/Extensions/ViewExtensions.cs

@LuigiMaestrelli No, Only changed it. You should pull this project and try to test it with sample project.

phamthanhtong avatar Mar 24 '17 15:03 phamthanhtong

@phamthanhtong, when use your patch, it works really good, but if users change the predictive mode while the keyboard is shown, the predictive bar will hide/make space between the keyboard & the entry. So, I have fixed that issue and here is the additional the code patched: KeyboardOverlapRenderer.cs

using System;
using Xamarin.Forms.Platform.iOS;
using Foundation;
using UIKit;
using Xamarin.Forms;
using CoreGraphics;
using KeyboardOverlap.Forms.Plugin.iOSUnified;
using System.Diagnostics;

[assembly: ExportRenderer(typeof(Page), typeof(KeyboardOverlapRenderer))]
namespace KeyboardOverlap.Forms.Plugin.iOSUnified
{
	[Preserve(AllMembers = true)]
	public class KeyboardOverlapRenderer : PageRenderer
	{
		NSObject _keyboardShowObserver;
		NSObject _keyboardHideObserver;
        NSObject _keyboardWillChangeObserver;
		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 = UIKeyboard.Notifications.ObserveWillShow(OnKeyboardShow);
            if (_keyboardHideObserver == null)
                _keyboardHideObserver = UIKeyboard.Notifications.ObserveWillHide(OnKeyboardHide);
            if (_keyboardWillChangeObserver == null)
                _keyboardWillChangeObserver = UIKeyboard.Notifications.ObserveWillChangeFrame(OnKeyboardChangeFrame);
		}


        void UnregisterForKeyboardNotifications()
		{
			_isKeyboardShown = false;
			if (_keyboardShowObserver != null)
			{
				_keyboardShowObserver.Dispose();
				_keyboardShowObserver = null;
			}

			if (_keyboardHideObserver != null)
			{
				_keyboardHideObserver.Dispose();
				_keyboardHideObserver = null;
			}
            if(_keyboardWillChangeObserver!=null)
            {
                _keyboardWillChangeObserver.Dispose();
                _keyboardWillChangeObserver = null;
            }
		}

        private void OnKeyboardShow(object sender,UIKeyboardEventArgs notification)
		{
			if (!IsViewLoaded || _isKeyboardShown)
				return;

			_isKeyboardShown = true;
			var activeView = View.FindFirstResponder();

			if (activeView == null)
				return;

            var keyboardFrame = notification.FrameEnd;
			var isOverlapping = activeView.IsKeyboardOverlapping(View, keyboardFrame);

			if (!isOverlapping)
				return;

			if (isOverlapping)
			{
				_activeViewBottom = activeView.GetViewRelativeBottom(View);
				ShiftPageUp(keyboardFrame.Height, _activeViewBottom);
			}
		}

        private void OnKeyboardHide(object sender, UIKeyboardEventArgs notification)
		{
			if (!IsViewLoaded)
				return;

			_isKeyboardShown = false;
            var keyboardFrame = notification.FrameEnd;

			if (_pageWasShiftedUp)
			{
				var activeView = View.FindFirstResponder();

				if (activeView == null)
					return;
                _activeViewBottom = activeView.GetViewRelativeBottom(View);
				ShiftPageDown(keyboardFrame.Height, _activeViewBottom);
			}
		}
        private void OnKeyboardChangeFrame(object sender, UIKeyboardEventArgs notification)
        {
            var keyboardFrame = notification.FrameEnd;
            if ((_isKeyboardShown) && (!keyboardFrame.Y.Equals(Element.Bounds.Height)))
            {
				if (!IsViewLoaded)
					return;

				var activeView = View.FindFirstResponder();

				if (activeView == null)
					return;
                var isOverlapping = activeView.IsKeyboardOverlapping(View, keyboardFrame);
                if(isOverlapping)
                {
                    _activeViewBottom = activeView.GetViewRelativeBottom(View);
                    if (keyboardFrame.Height > notification.FrameBegin.Height)
                        ShiftPageUp(keyboardFrame.Height - notification.FrameBegin.Height, Element.Bounds.Height, false);
                    else
                        ShiftPageDown(notification.FrameBegin.Height - keyboardFrame.Height, Element.Bounds.Height, false);
                }
			}
            else return;

        }
		private void ShiftPageUp(nfloat keyboardHeight, double activeViewBottom,bool willResetLater=true)
		{
			var pageFrame = Element.Bounds;

			var newY = pageFrame.Y + CalculateShiftByAmount(pageFrame.Height, keyboardHeight, activeViewBottom);

			Element.LayoutTo(new Rectangle(pageFrame.X, newY,
				pageFrame.Width, pageFrame.Height));
            if (willResetLater)
                _pageWasShiftedUp = true;
		}

		private void ShiftPageDown(nfloat keyboardHeight, double activeViewBottom,bool willResetLater=true)
		{
			var pageFrame = Element.Bounds;

			var newY = pageFrame.Y - CalculateShiftByAmount(pageFrame.Height, keyboardHeight, activeViewBottom);

			Element.LayoutTo(new Rectangle(pageFrame.X, newY,
				pageFrame.Width, pageFrame.Height));
            if (willResetLater)
                _pageWasShiftedUp = false;
		}

		private double CalculateShiftByAmount(double pageHeight, nfloat keyboardHeight, double activeViewBottom)
		{
			return (pageHeight - activeViewBottom) - keyboardHeight;
		}
	}
}

TrungNguyen1909 avatar May 30 '17 09:05 TrungNguyen1909

I have this problem when Editor with OnTextChanged has InvalidateMeasure is use in NavigationPage, only NavigationPage. Here is example Link

gaffkins avatar May 09 '18 11:05 gaffkins

I have similar issue like @gaffkins facing. Please suggest.

nanjibiztech avatar Jun 05 '19 11:06 nanjibiztech