In iOS, Xamarin.Forms ridimensiona lo schermo quando viene visualizzata la tastiera quando il nodo radice è un ScrollView
. Ma quando il nodo radice non è un ScrollView
, la tastiera nasconde parte dell'interfaccia utente. Come fai a evitare che ciò accada?Come posso impedire alla tastiera di coprire l'interfaccia utente anziché ridimensionarla?
risposta
Il modo per risolvere il problema è con un renderer personalizzato che ascolta la tastiera che mostra e aggiunge il padding mentre è lì.
Nel progetto PCL, KeyboardResizingAwareContentPage.cs
:
using Xamarin.Forms;
public class KeyboardResizingAwareContentPage : ContentPage {
public bool CancelsTouchesInView = true;
}
Nel progetto 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);
}
}
}
}
Questo Xamarin Forum question discute.
Inoltre, se lo volete in Android utilizzando Xamarin/Forms si può solo impostare questo nella vostra attività principale:
[Activity(WindowSoftInputMode = Android.Views.SoftInput.AdjustResize)]
public class MainActivity
...
ho scoperto che il KeyboardOverlap plugin funziona meglio rispetto alla soluzione di Anthony.
Questo è come lo uso:
- Creare un renderer personalizzato
public class KeyboardResizingAwareContentPage : ContentPage
{
}
- Implementare il renderer personalizzato su iOS. Ecco la parte importante di paulpatarinski Codice:
[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;
}
}
E le estensioni mancanti:
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;
}
}
- Utilizzare la pagina personalizzata renderer
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>
Tutti i crediti per questo vanno a Paul! Grazie per questo!
Anche questa soluzione viene attivata (dai log di produzione): System.ObjectDisposedException: impossibile accedere a un oggetto disposto.Nome oggetto: "IosKeyboardFixPageRenderer". –
_isKeyboardShown = falso; dovrebbe essere spostato alla fine del metodo UnregisterForKeyboardNotifications –
Il risultato è soddisfacente, ma come posso fare per nascondere la tastiera? Perché se tocco fuori dalla tastiera non si sta chiudendo ... –
@ Fran_gg7 usa ResignFirstResponder(). Quindi "il tuo controllo che attiva la tastiera" ResignFirstResponder(); – Sturla
Xamarin Forms dovrebbe avere un riconoscitore di gesti nella pagina che chiama automaticamente "ResignFirstResponder" per te. A volte non vuoi questo comportamento, che è ciò che è il booleano di 'CancelsTouchesInView'; impostarlo su false per annullare il comportamento predefinito di Xamarin Forms. –