如何防止键盘覆盖我的 UI 而不是调整它的大小?
Posted
技术标签:
【中文标题】如何防止键盘覆盖我的 UI 而不是调整它的大小?【英文标题】:How do I keep the keyboard from covering my UI instead of resizing it? 【发布时间】:2015-09-19 06:30:50 【问题描述】:在 ios 中,当根节点为 ScrollView
时,Xamarin.Forms 会在键盘出现时调整屏幕大小。但是当根节点不是ScrollView
时,键盘会隐藏部分 UI。你如何防止这种情况发生?
【问题讨论】:
【参考方案1】:解决此问题的方法是使用自定义渲染器,该渲染器侦听键盘出现并在键盘出现时添加填充。
在您的 PCL 项目中,KeyboardResizingAwareContentPage.cs
:
using Xamarin.Forms;
public class KeyboardResizingAwareContentPage : ContentPage
public bool CancelsTouchesInView = true;
在您的 iOS 项目中,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);
【讨论】:
结果很好,但是如何隐藏键盘呢?因为如果我在键盘外点击不会关闭... @Fran_gg7 使用 ResignFirstResponder()。所以“激活键盘的控件” ResignFirstResponder(); Xamarin Forms 应该在页面上有一个手势识别器,它会自动为您调用ResignFirstResponder
。有时您不想要这种行为,这就是 CancelsTouchesInView
布尔值的用途;将其设置为 false 以取消 Xamarin Forms 的默认行为。
我来自 Xamarin 论坛。我真的很感激你的一段代码。 :)
@AnthonyMills 这个解决方案很棒,但是有一个问题。如果其中一个条目被标记为 IsPassword,则页面滚动不规律:)【参考方案2】:
我发现KeyboardOverlap plugin 比 Anthony 的解决方案效果更好。
我就是这样使用它的:
-
创建自定义渲染器
public class KeyboardResizingAwareContentPage : ContentPage
-
在 iOS 上实现自定义渲染器。这是paulpatarinski代码的重要部分:
[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;
还有缺少的扩展:
public static class ViewExtensions
/// <summary>
/// Find the first responder in the <paramref name="view"/>'s 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;
-
使用自定义页面渲染器
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>
这一切归功于保罗!谢谢!
【讨论】:
这个解决方案也会触发(来自生产日志):System.ObjectDisposedException:无法访问已处置的对象。对象名称:'IosKeyboardFixPageRenderer'。 _isKeyboardShown = false;应该在方法 UnregisterForKeyboardNotifications 的末尾移动【参考方案3】:Xamarin Forum question 讨论它。
此外,如果您希望在 android 中使用 Xamarin / Forms,您可以在主 Activity 中进行设置:
[Activity(WindowSoftInputMode = Android.Views.SoftInput.AdjustResize)]
public class MainActivity
...
【讨论】:
【参考方案4】:对于Android,在MainActivity
LoadApplication(new App());
之后添加以下代码
App.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().
UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
【讨论】:
以上是关于如何防止键盘覆盖我的 UI 而不是调整它的大小?的主要内容,如果未能解决你的问题,请参考以下文章
显示键盘时防止 DialogFragment 调整大小/折叠
我们如何防止当我调整窗口大小时,我的图像使用 HTML-CSS 移动?
设置高 DPI 时防止我的 Win32 应用程序的 UI 元素按比例放大