diff options
Diffstat (limited to 'Xamarin.Forms.Platform.iOS/EventTracker.cs')
-rw-r--r-- | Xamarin.Forms.Platform.iOS/EventTracker.cs | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.iOS/EventTracker.cs b/Xamarin.Forms.Platform.iOS/EventTracker.cs new file mode 100644 index 00000000..16ccd505 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/EventTracker.cs @@ -0,0 +1,303 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +#if __UNIFIED__ +using UIKit; +#else +using MonoTouch.UIKit; +#endif +#if __UNIFIED__ +using RectangleF = CoreGraphics.CGRect; +using SizeF = CoreGraphics.CGSize; +using PointF = CoreGraphics.CGPoint; + +#else +using nfloat=System.Single; +using nint=System.Int32; +using nuint=System.UInt32; +#endif + +namespace Xamarin.Forms.Platform.iOS +{ + public class EventTracker : IDisposable + { + readonly NotifyCollectionChangedEventHandler _collectionChangedHandler; + + readonly Dictionary<IGestureRecognizer, UIGestureRecognizer> _gestureRecognizers = new Dictionary<IGestureRecognizer, UIGestureRecognizer>(); + + readonly IVisualElementRenderer _renderer; + bool _disposed; + UIView _handler; + + double _previousScale = 1.0; + UITouchEventArgs _shouldReceive; + + public EventTracker(IVisualElementRenderer renderer) + { + if (renderer == null) + throw new ArgumentNullException("renderer"); + + _collectionChangedHandler = ModelGestureRecognizersOnCollectionChanged; + + _renderer = renderer; + _renderer.ElementChanged += OnElementChanged; + } + + ObservableCollection<IGestureRecognizer> ElementGestureRecognizers + { + get + { + if (_renderer?.Element is View) + return ((View)_renderer.Element).GestureRecognizers as ObservableCollection<IGestureRecognizer>; + return null; + } + } + + public void Dispose() + { + if (_disposed) + return; + + _disposed = true; + + foreach (var kvp in _gestureRecognizers) + { + _handler.RemoveGestureRecognizer(kvp.Value); + kvp.Value.Dispose(); + } + + _gestureRecognizers.Clear(); + + if (ElementGestureRecognizers != null) + ElementGestureRecognizers.CollectionChanged -= _collectionChangedHandler; + + _handler = null; + } + + public void LoadEvents(UIView handler) + { + if (_disposed) + throw new ObjectDisposedException(null); + + _shouldReceive = (r, t) => t.View is IVisualElementRenderer; + + _handler = handler; + OnElementChanged(this, new VisualElementChangedEventArgs(null, _renderer.Element)); + } + + protected virtual UIGestureRecognizer GetNativeRecognizer(IGestureRecognizer recognizer) + { + if (recognizer == null) + return null; + + var weakRecognizer = new WeakReference(recognizer); + var weakEventTracker = new WeakReference(this); + + var tapRecognizer = recognizer as TapGestureRecognizer; + if (tapRecognizer != null) + { + var uiRecognizer = CreateTapRecognizer(1, tapRecognizer.NumberOfTapsRequired, r => + { + var tapGestureRecognizer = weakRecognizer.Target as TapGestureRecognizer; + var eventTracker = weakEventTracker.Target as EventTracker; + var view = eventTracker?._renderer?.Element as View; + + if (tapGestureRecognizer != null && view != null) + tapGestureRecognizer.SendTapped(view); + }); + return uiRecognizer; + } + + var pinchRecognizer = recognizer as PinchGestureRecognizer; + if (pinchRecognizer != null) + { + double startingScale = 1; + var uiRecognizer = CreatePinchRecognizer(r => + { + var pinchGestureRecognizer = weakRecognizer.Target as IPinchGestureController; + var eventTracker = weakEventTracker.Target as EventTracker; + var view = eventTracker?._renderer?.Element as View; + + if (pinchGestureRecognizer != null && eventTracker != null && view != null) + { + var oldScale = eventTracker._previousScale; + var originPoint = r.LocationInView(null); + originPoint = UIApplication.SharedApplication.KeyWindow.ConvertPointToView(originPoint, eventTracker._renderer.NativeView); + var scaledPoint = new Point(originPoint.X / view.Width, originPoint.Y / view.Height); + + switch (r.State) + { + case UIGestureRecognizerState.Began: + if (r.NumberOfTouches < 2) + return; + pinchGestureRecognizer.SendPinchStarted(view, scaledPoint); + startingScale = view.Scale; + break; + case UIGestureRecognizerState.Changed: + if (r.NumberOfTouches < 2 && pinchGestureRecognizer.IsPinching) + { + r.State = UIGestureRecognizerState.Ended; + pinchGestureRecognizer.SendPinchEnded(view); + return; + } + + var delta = 1.0; + var dif = Math.Abs(r.Scale - oldScale) * startingScale; + if (oldScale < r.Scale) + delta = 1 + dif; + if (oldScale > r.Scale) + delta = 1 - dif; + + pinchGestureRecognizer.SendPinch(view, delta, scaledPoint); + eventTracker._previousScale = r.Scale; + break; + case UIGestureRecognizerState.Cancelled: + case UIGestureRecognizerState.Failed: + if (pinchGestureRecognizer.IsPinching) + pinchGestureRecognizer.SendPinchCanceled(view); + break; + case UIGestureRecognizerState.Ended: + if (pinchGestureRecognizer.IsPinching) + pinchGestureRecognizer.SendPinchEnded(view); + eventTracker._previousScale = 1; + break; + } + } + }); + return uiRecognizer; + } + + var panRecognizer = recognizer as PanGestureRecognizer; + if (panRecognizer != null) + { + var uiRecognizer = CreatePanRecognizer(panRecognizer.TouchPoints, r => + { + var eventTracker = weakEventTracker.Target as EventTracker; + var view = eventTracker?._renderer?.Element as View; + + var panGestureRecognizer = weakRecognizer.Target as IPanGestureController; + if (panGestureRecognizer != null && view != null) + { + switch (r.State) + { + case UIGestureRecognizerState.Began: + if (r.NumberOfTouches != panRecognizer.TouchPoints) + return; + panGestureRecognizer.SendPanStarted(view, Application.Current.PanGestureId); + break; + case UIGestureRecognizerState.Changed: + if (r.NumberOfTouches != panRecognizer.TouchPoints) + { + r.State = UIGestureRecognizerState.Ended; + panGestureRecognizer.SendPanCompleted(view, Application.Current.PanGestureId); + Application.Current.PanGestureId++; + return; + } + var translationInView = r.TranslationInView(_handler); + panGestureRecognizer.SendPan(view, translationInView.X, translationInView.Y, Application.Current.PanGestureId); + break; + case UIGestureRecognizerState.Cancelled: + case UIGestureRecognizerState.Failed: + panGestureRecognizer.SendPanCanceled(view, Application.Current.PanGestureId); + Application.Current.PanGestureId++; + break; + case UIGestureRecognizerState.Ended: + if (r.NumberOfTouches != panRecognizer.TouchPoints) + { + panGestureRecognizer.SendPanCompleted(view, Application.Current.PanGestureId); + Application.Current.PanGestureId++; + } + break; + } + } + }); + return uiRecognizer; + } + + return null; + } + + UIPanGestureRecognizer CreatePanRecognizer(int numTouches, Action<UIPanGestureRecognizer> action) + { + var result = new UIPanGestureRecognizer(action); + result.MinimumNumberOfTouches = result.MaximumNumberOfTouches = (uint)numTouches; + return result; + } + + UIPinchGestureRecognizer CreatePinchRecognizer(Action<UIPinchGestureRecognizer> action) + { + var result = new UIPinchGestureRecognizer(action); + return result; + } + + UITapGestureRecognizer CreateTapRecognizer(int numFingers, int numTaps, Action<UITapGestureRecognizer> action) + { + var result = new UITapGestureRecognizer(action); + result.NumberOfTouchesRequired = (uint)numFingers; + result.NumberOfTapsRequired = (uint)numTaps; + return result; + } + + void LoadRecognizers() + { + if (ElementGestureRecognizers == null) + return; + + foreach (var recognizer in ElementGestureRecognizers) + { + if (_gestureRecognizers.ContainsKey(recognizer)) + continue; + + var nativeRecognizer = GetNativeRecognizer(recognizer); + if (nativeRecognizer != null) + { + nativeRecognizer.ShouldReceiveTouch = _shouldReceive; + _handler.AddGestureRecognizer(nativeRecognizer); + + _gestureRecognizers[recognizer] = nativeRecognizer; + } + } + + var toRemove = _gestureRecognizers.Keys.Where(key => !ElementGestureRecognizers.Contains(key)).ToArray(); + foreach (var gestureRecognizer in toRemove) + { + var uiRecognizer = _gestureRecognizers[gestureRecognizer]; + _gestureRecognizers.Remove(gestureRecognizer); + + _handler.RemoveGestureRecognizer(uiRecognizer); + uiRecognizer.Dispose(); + } + } + + void ModelGestureRecognizersOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) + { + LoadRecognizers(); + } + + void OnElementChanged(object sender, VisualElementChangedEventArgs e) + { + if (e.OldElement != null) + { + // unhook + var oldView = e.OldElement as View; + if (oldView != null) + { + var oldRecognizers = (ObservableCollection<IGestureRecognizer>)oldView.GestureRecognizers; + oldRecognizers.CollectionChanged -= _collectionChangedHandler; + } + } + + if (e.NewElement != null) + { + // hook + if (ElementGestureRecognizers != null) + { + ElementGestureRecognizers.CollectionChanged += _collectionChangedHandler; + LoadRecognizers(); + } + } + } + } +}
\ No newline at end of file |