diff --git a/src/iOS/Perspex.iOS/Perspex.iOS.csproj b/src/iOS/Perspex.iOS/Perspex.iOS.csproj
index 1a5866908b..0d670ac5ed 100644
--- a/src/iOS/Perspex.iOS/Perspex.iOS.csproj
+++ b/src/iOS/Perspex.iOS/Perspex.iOS.csproj
@@ -42,6 +42,7 @@
+
diff --git a/src/iOS/Perspex.iOS/PerspexView.cs b/src/iOS/Perspex.iOS/PerspexView.cs
index 1bea986c98..4adfb98790 100644
--- a/src/iOS/Perspex.iOS/PerspexView.cs
+++ b/src/iOS/Perspex.iOS/PerspexView.cs
@@ -14,26 +14,41 @@ using Perspex.Media;
using Perspex.Platform;
using Perspex.Skia.iOS;
using UIKit;
+using Perspex.iOS.Specific;
+using ObjCRuntime;
namespace Perspex.iOS
{
+ [Adopts("UIKeyInput")]
class PerspexView : SkiaView, IWindowImpl
{
private readonly UIWindow _window;
private readonly UIViewController _controller;
private IInputRoot _inputRoot;
+ private readonly KeyboardEventsHelper _keyboardHelper;
public PerspexView(UIWindow window, UIViewController controller) : base(onFrame => PlatformThreadingInterface.Instance.Render = onFrame)
{
if (controller == null) throw new ArgumentNullException(nameof(controller));
_window = window;
_controller = controller;
+ _keyboardHelper = new KeyboardEventsHelper(this);
AutoresizingMask = UIViewAutoresizing.All;
AutoFit();
UIApplication.Notifications.ObserveDidChangeStatusBarOrientation(delegate { AutoFit(); });
UIApplication.Notifications.ObserveDidChangeStatusBarFrame(delegate { AutoFit(); });
}
+ [Export("hasText")]
+ bool HasText => _keyboardHelper.HasText();
+
+ [Export("insertText:")]
+ void InsertText(string text) => _keyboardHelper.InsertText(text);
+
+ [Export("deleteBackward")]
+ void DeleteBackward() => _keyboardHelper.DeleteBackward();
+
+ public override bool CanBecomeFirstResponder => _keyboardHelper.CanBecomeFirstResponder();
void AutoFit()
{
@@ -89,6 +104,7 @@ namespace Perspex.iOS
public void Show()
{
+ _keyboardHelper.ActivateAutoShowKeybord();
}
public Size MaxClientSize => Bounds.Size.ToPerspex();
@@ -152,8 +168,14 @@ namespace Perspex.iOS
RawMouseEventType.Move, location, InputModifiers.LeftMouseButton));
else
{
- Input?.Invoke(new RawMouseWheelEventArgs(PerspexAppDelegate.MouseDevice, (uint) touch.Timestamp,
- _inputRoot, location, location - _touchLastPoint, InputModifiers.LeftMouseButton));
+ double x = location.X - _touchLastPoint.X;
+ double y = location.Y - _touchLastPoint.Y;
+ double correction = 0.02;
+ var scale = PerspexLocator.Current.GetService().RenderScalingFactor;
+ scale = 1;
+
+ Input?.Invoke(new RawMouseWheelEventArgs(PerspexAppDelegate.MouseDevice, (uint)touch.Timestamp,
+ _inputRoot, location, new Vector(x * correction / scale, y * correction / scale), InputModifiers.LeftMouseButton));
}
_touchLastPoint = location;
}
diff --git a/src/iOS/Perspex.iOS/Specific/KeyboardEventsHelper.cs b/src/iOS/Perspex.iOS/Specific/KeyboardEventsHelper.cs
new file mode 100644
index 0000000000..872f9cd37d
--- /dev/null
+++ b/src/iOS/Perspex.iOS/Specific/KeyboardEventsHelper.cs
@@ -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
+{
+ ///
+ /// 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();
+ ///
+ /// View that needs keyboard events and show/hide keyboard
+ internal class KeyboardEventsHelper 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().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;
+ }
+
+ ///
+ /// HandleEvents in order to suspend keyboard notifications or resume it
+ ///
+ 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;
+ }
+ }
+}
\ No newline at end of file