3 changed files with 172 additions and 2 deletions
@ -0,0 +1,147 @@ |
|||||
|
using ObjCRuntime; |
||||
|
using Perspex.Controls; |
||||
|
using Perspex.Input; |
||||
|
using Perspex.Input.Raw; |
||||
|
using Perspex.Platform; |
||||
|
using System; |
||||
|
using System.ComponentModel; |
||||
|
using System.Linq; |
||||
|
using UIKit; |
||||
|
|
||||
|
namespace Perspex.iOS.Specific |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// In order to have properly handle of keyboard event in iOS View should already made some things in the View:
|
||||
|
/// 1. Adopt the UIKeyInput protocol - add [Adopts("UIKeyInput")] to your view class
|
||||
|
/// 2. Implement all the methods required by UIKeyInput:
|
||||
|
/// 2.1 Implement HasText
|
||||
|
/// example:
|
||||
|
/// [Export("hasText")]
|
||||
|
/// bool HasText => _keyboardHelper.HasText()
|
||||
|
/// 2.2 Implement InsertText
|
||||
|
/// example:
|
||||
|
/// [Export("insertText:")]
|
||||
|
/// void InsertText(string text) => _keyboardHelper.InsertText(text);
|
||||
|
/// 2.3 Implement InsertText
|
||||
|
/// example:
|
||||
|
/// [Export("deleteBackward")]
|
||||
|
/// void DeleteBackward() => _keyboardHelper.DeleteBackward();
|
||||
|
/// 3.Let iOS know that this can become a first responder:
|
||||
|
/// public override bool CanBecomeFirstResponder => _keyboardHelper.CanBecomeFirstResponder();
|
||||
|
/// or
|
||||
|
/// public override bool CanBecomeFirstResponder { get { return true; } }
|
||||
|
///
|
||||
|
/// 4. To show keyboard:
|
||||
|
/// view.BecomeFirstResponder();
|
||||
|
/// 5. To hide keyboard
|
||||
|
/// view.ResignFirstResponder();
|
||||
|
/// </summary>
|
||||
|
/// <typeparam name="TView">View that needs keyboard events and show/hide keyboard</typeparam>
|
||||
|
internal class KeyboardEventsHelper<TView> where TView : UIView, IWindowImpl |
||||
|
{ |
||||
|
private TView _view; |
||||
|
private IInputElement _lastFocusedElement; |
||||
|
|
||||
|
public KeyboardEventsHelper(TView view) |
||||
|
{ |
||||
|
_view = view; |
||||
|
|
||||
|
var uiKeyInputAttribute = view.GetType().GetCustomAttributes(typeof(AdoptsAttribute), true).OfType<AdoptsAttribute>().Where(a => a.ProtocolType == "UIKeyInput").FirstOrDefault(); |
||||
|
|
||||
|
if (uiKeyInputAttribute == null) throw new NotSupportedException($"View class {typeof(TView).Name} should have class attribute - [Adopts(\"UIKeyInput\")] in order to access keyboard events!"); |
||||
|
|
||||
|
HandleEvents = true; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// HandleEvents in order to suspend keyboard notifications or resume it
|
||||
|
/// </summary>
|
||||
|
public bool HandleEvents { get; set; } |
||||
|
|
||||
|
public bool HasText() => false; |
||||
|
|
||||
|
public bool CanBecomeFirstResponder() => true; |
||||
|
|
||||
|
public void DeleteBackward() |
||||
|
{ |
||||
|
HandleKey(Key.Back, RawKeyEventType.KeyDown); |
||||
|
HandleKey(Key.Back, RawKeyEventType.KeyUp); |
||||
|
} |
||||
|
|
||||
|
public void InsertText(string text) |
||||
|
{ |
||||
|
var rawTextEvent = new RawTextInputEventArgs(KeyboardDevice.Instance, (uint)DateTime.Now.Ticks, text); |
||||
|
_view.Input(rawTextEvent); |
||||
|
} |
||||
|
|
||||
|
private void HandleKey(Key key, RawKeyEventType type) |
||||
|
{ |
||||
|
var rawKeyEvent = new RawKeyEventArgs(KeyboardDevice.Instance, (uint)DateTime.Now.Ticks, type, key, InputModifiers.None); |
||||
|
_view.Input(rawKeyEvent); |
||||
|
} |
||||
|
|
||||
|
//currently not found a way to get InputModifiers state
|
||||
|
//private static InputModifiers GetModifierKeys(object e)
|
||||
|
//{
|
||||
|
// var im = InputModifiers.None;
|
||||
|
// //if (IsCtrlPressed) rv |= InputModifiers.Control;
|
||||
|
// //if (IsShiftPressed) rv |= InputModifiers.Shift;
|
||||
|
|
||||
|
// return im;
|
||||
|
//}
|
||||
|
|
||||
|
private bool NeedsKeyboard(IInputElement element) |
||||
|
{ |
||||
|
//may be some other elements
|
||||
|
return element is TextBox; |
||||
|
} |
||||
|
|
||||
|
private void TryShowHideKeyboard(IInputElement element, bool value) |
||||
|
{ |
||||
|
if (value) |
||||
|
{ |
||||
|
_view.BecomeFirstResponder(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
_view.ResignFirstResponder(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void UpdateKeyboardState(IInputElement element) |
||||
|
{ |
||||
|
var focusedElement = element; |
||||
|
bool oldValue = NeedsKeyboard(_lastFocusedElement); |
||||
|
bool newValue = NeedsKeyboard(focusedElement); |
||||
|
|
||||
|
if (newValue != oldValue || newValue) |
||||
|
{ |
||||
|
TryShowHideKeyboard(focusedElement, newValue); |
||||
|
} |
||||
|
|
||||
|
_lastFocusedElement = element; |
||||
|
} |
||||
|
|
||||
|
public void ActivateAutoShowKeybord() |
||||
|
{ |
||||
|
var kbDevice = (KeyboardDevice.Instance as INotifyPropertyChanged); |
||||
|
|
||||
|
//just in case we've called more than once the method
|
||||
|
kbDevice.PropertyChanged -= KeyboardDevice_PropertyChanged; |
||||
|
kbDevice.PropertyChanged += KeyboardDevice_PropertyChanged; |
||||
|
} |
||||
|
|
||||
|
private void KeyboardDevice_PropertyChanged(object sender, PropertyChangedEventArgs e) |
||||
|
{ |
||||
|
if (e.PropertyName == nameof(KeyboardDevice.FocusedElement)) |
||||
|
{ |
||||
|
UpdateKeyboardState(KeyboardDevice.Instance.FocusedElement); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void Dispose() |
||||
|
{ |
||||
|
HandleEvents = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue