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