summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs401
1 files changed, 401 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs
new file mode 100644
index 00000000..070a0389
--- /dev/null
+++ b/Xamarin.Forms.Platform.iOS/Renderers/PhoneMasterDetailRenderer.cs
@@ -0,0 +1,401 @@
+using System;
+using System.Linq;
+using System.Drawing;
+using System.ComponentModel;
+#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 PhoneMasterDetailRenderer : UIViewController, IVisualElementRenderer
+ {
+ UIView _clickOffView;
+ UIViewController _detailController;
+
+ bool _disposed;
+ EventTracker _events;
+
+ UIViewController _masterController;
+
+ UIPanGestureRecognizer _panGesture;
+
+ bool _presented;
+ UIGestureRecognizer _tapGesture;
+
+ VisualElementTracker _tracker;
+
+ public PhoneMasterDetailRenderer()
+ {
+ if (!Forms.IsiOS7OrNewer)
+ WantsFullScreenLayout = true;
+ }
+
+ bool Presented
+ {
+ get { return _presented; }
+ set
+ {
+ if (_presented == value)
+ return;
+ _presented = value;
+ LayoutChildren(true);
+ if (value)
+ AddClickOffView();
+ else
+ RemoveClickOffView();
+
+ ((IElementController)Element).SetValueFromRenderer(MasterDetailPage.IsPresentedProperty, value);
+ }
+ }
+
+ public VisualElement Element { get; private set; }
+
+ public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
+
+ public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
+ {
+ return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
+ }
+
+ public UIView NativeView
+ {
+ get { return View; }
+ }
+
+ public void SetElement(VisualElement element)
+ {
+ var oldElement = Element;
+ Element = element;
+ Element.SizeChanged += PageOnSizeChanged;
+
+ _masterController = new ChildViewController();
+ _detailController = new ChildViewController();
+
+ _clickOffView = new UIView();
+ _clickOffView.BackgroundColor = new Color(0, 0, 0, 0).ToUIColor();
+
+ Presented = ((MasterDetailPage)Element).IsPresented;
+
+ OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+
+ if (element != null)
+ element.SendViewInitialized(NativeView);
+ }
+
+ public void SetElementSize(Size size)
+ {
+ Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height));
+ }
+
+ public UIViewController ViewController
+ {
+ get { return this; }
+ }
+
+ public override void ViewDidAppear(bool animated)
+ {
+ base.ViewDidAppear(animated);
+ ((Page)Element).SendAppearing();
+ }
+
+ public override void ViewDidDisappear(bool animated)
+ {
+ base.ViewDidDisappear(animated);
+ ((Page)Element).SendDisappearing();
+ }
+
+ public override void ViewDidLayoutSubviews()
+ {
+ base.ViewDidLayoutSubviews();
+
+ LayoutChildren(false);
+ }
+
+ public override void ViewDidLoad()
+ {
+ base.ViewDidLoad();
+
+ _tracker = new VisualElementTracker(this);
+ _events = new EventTracker(this);
+ _events.LoadEvents(View);
+
+ ((MasterDetailPage)Element).PropertyChanged += HandlePropertyChanged;
+
+ _tapGesture = new UITapGestureRecognizer(() =>
+ {
+ if (Presented)
+ Presented = false;
+ });
+ _clickOffView.AddGestureRecognizer(_tapGesture);
+
+ PackContainers();
+ UpdateMasterDetailContainers();
+
+ UpdateBackground();
+
+ UpdatePanGesture();
+ }
+
+ public override void WillRotate(UIInterfaceOrientation toInterfaceOrientation, double duration)
+ {
+ if (!((MasterDetailPage)Element).ShouldShowSplitMode && _presented)
+ Presented = false;
+
+ base.WillRotate(toInterfaceOrientation, duration);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && !_disposed)
+ {
+ Element.SizeChanged -= PageOnSizeChanged;
+ Element.PropertyChanged -= HandlePropertyChanged;
+
+ if (_tracker != null)
+ {
+ _tracker.Dispose();
+ _tracker = null;
+ }
+
+ if (_events != null)
+ {
+ _events.Dispose();
+ _events = null;
+ }
+
+ if (_tapGesture != null)
+ {
+ if (_clickOffView != null && _clickOffView.GestureRecognizers.Contains(_panGesture))
+ {
+ _clickOffView.GestureRecognizers.Remove(_tapGesture);
+ _clickOffView.Dispose();
+ }
+ _tapGesture.Dispose();
+ }
+ if (_panGesture != null)
+ {
+ if (View != null && View.GestureRecognizers.Contains(_panGesture))
+ View.GestureRecognizers.Remove(_panGesture);
+ _panGesture.Dispose();
+ }
+
+ EmptyContainers();
+
+ ((Page)Element).SendDisappearing();
+
+ _disposed = true;
+ }
+
+ base.Dispose(disposing);
+ }
+
+ protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ var changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ void AddClickOffView()
+ {
+ View.Add(_clickOffView);
+ _clickOffView.Frame = _detailController.View.Frame;
+ }
+
+ void EmptyContainers()
+ {
+ foreach (var child in _detailController.View.Subviews.Concat(_masterController.View.Subviews))
+ child.RemoveFromSuperview();
+
+ foreach (var vc in _detailController.ChildViewControllers.Concat(_masterController.ChildViewControllers))
+ vc.RemoveFromParentViewController();
+ }
+
+ void HandleMasterPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == Page.IconProperty.PropertyName || e.PropertyName == Page.TitleProperty.PropertyName)
+ MessagingCenter.Send<IVisualElementRenderer>(this, NavigationRenderer.UpdateToolbarButtons);
+ }
+
+ void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "Master" || e.PropertyName == "Detail")
+ UpdateMasterDetailContainers();
+ else if (e.PropertyName == MasterDetailPage.IsPresentedProperty.PropertyName)
+ Presented = ((MasterDetailPage)Element).IsPresented;
+ else if (e.PropertyName == MasterDetailPage.IsGestureEnabledProperty.PropertyName)
+ UpdatePanGesture();
+ else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
+ UpdateBackground();
+ else if (e.PropertyName == Page.BackgroundImageProperty.PropertyName)
+ UpdateBackground();
+ }
+
+ void LayoutChildren(bool animated)
+ {
+ var frame = Element.Bounds.ToRectangleF();
+ var masterFrame = frame;
+ masterFrame.Width = (int)(Math.Min(masterFrame.Width, masterFrame.Height) * 0.8);
+
+ _masterController.View.Frame = masterFrame;
+
+ var target = frame;
+ if (Presented)
+ target.X += masterFrame.Width;
+
+ if (animated)
+ {
+ UIView.BeginAnimations("Flyout");
+ var view = _detailController.View;
+ view.Frame = target;
+ UIView.SetAnimationCurve(UIViewAnimationCurve.EaseOut);
+ UIView.SetAnimationDuration(250);
+ UIView.CommitAnimations();
+ }
+ else
+ _detailController.View.Frame = target;
+
+ ((MasterDetailPage)Element).MasterBounds = new Rectangle(0, 0, masterFrame.Width, masterFrame.Height);
+ ((MasterDetailPage)Element).DetailBounds = new Rectangle(0, 0, frame.Width, frame.Height);
+
+ if (Presented)
+ _clickOffView.Frame = _detailController.View.Frame;
+ }
+
+ void PackContainers()
+ {
+ _detailController.View.BackgroundColor = new UIColor(1, 1, 1, 1);
+ View.AddSubview(_masterController.View);
+ View.AddSubview(_detailController.View);
+
+ AddChildViewController(_masterController);
+ AddChildViewController(_detailController);
+ }
+
+ void PageOnSizeChanged(object sender, EventArgs eventArgs)
+ {
+ LayoutChildren(false);
+ }
+
+ void RemoveClickOffView()
+ {
+ _clickOffView.RemoveFromSuperview();
+ }
+
+ void UpdateBackground()
+ {
+ if (!string.IsNullOrEmpty(((Page)Element).BackgroundImage))
+ View.BackgroundColor = UIColor.FromPatternImage(UIImage.FromBundle(((Page)Element).BackgroundImage));
+ else if (Element.BackgroundColor == Color.Default)
+ View.BackgroundColor = UIColor.White;
+ else
+ View.BackgroundColor = Element.BackgroundColor.ToUIColor();
+ }
+
+ void UpdateMasterDetailContainers()
+ {
+ ((MasterDetailPage)Element).Master.PropertyChanged -= HandleMasterPropertyChanged;
+
+ EmptyContainers();
+
+ if (Platform.GetRenderer(((MasterDetailPage)Element).Master) == null)
+ Platform.SetRenderer(((MasterDetailPage)Element).Master, Platform.CreateRenderer(((MasterDetailPage)Element).Master));
+ if (Platform.GetRenderer(((MasterDetailPage)Element).Detail) == null)
+ Platform.SetRenderer(((MasterDetailPage)Element).Detail, Platform.CreateRenderer(((MasterDetailPage)Element).Detail));
+
+ var masterRenderer = Platform.GetRenderer(((MasterDetailPage)Element).Master);
+ var detailRenderer = Platform.GetRenderer(((MasterDetailPage)Element).Detail);
+
+ ((MasterDetailPage)Element).Master.PropertyChanged += HandleMasterPropertyChanged;
+
+ _masterController.View.AddSubview(masterRenderer.NativeView);
+ _masterController.AddChildViewController(masterRenderer.ViewController);
+
+ _detailController.View.AddSubview(detailRenderer.NativeView);
+ _detailController.AddChildViewController(detailRenderer.ViewController);
+ }
+
+ void UpdatePanGesture()
+ {
+ var model = (MasterDetailPage)Element;
+ if (!model.IsGestureEnabled)
+ {
+ if (_panGesture != null)
+ View.RemoveGestureRecognizer(_panGesture);
+ return;
+ }
+
+ if (_panGesture != null)
+ {
+ View.AddGestureRecognizer(_panGesture);
+ return;
+ }
+
+ UITouchEventArgs shouldRecieve = (g, t) => !(t.View is UISlider);
+ var center = new PointF();
+ _panGesture = new UIPanGestureRecognizer(g =>
+ {
+ switch (g.State)
+ {
+ case UIGestureRecognizerState.Began:
+ center = g.LocationInView(g.View);
+ break;
+ case UIGestureRecognizerState.Changed:
+ var currentPosition = g.LocationInView(g.View);
+ var motion = currentPosition.X - center.X;
+ var detailView = _detailController.View;
+ var targetFrame = detailView.Frame;
+ if (Presented)
+ targetFrame.X = (nfloat)Math.Max(0, _masterController.View.Frame.Width + Math.Min(0, motion));
+ else
+ targetFrame.X = (nfloat)Math.Min(_masterController.View.Frame.Width, Math.Max(0, motion));
+ detailView.Frame = targetFrame;
+ break;
+ case UIGestureRecognizerState.Ended:
+ var detailFrame = _detailController.View.Frame;
+ var masterFrame = _masterController.View.Frame;
+ if (Presented)
+ {
+ if (detailFrame.X < masterFrame.Width * .75)
+ Presented = false;
+ else
+ LayoutChildren(true);
+ }
+ else
+ {
+ if (detailFrame.X > masterFrame.Width * .25)
+ Presented = true;
+ else
+ LayoutChildren(true);
+ }
+ break;
+ }
+ });
+ _panGesture.ShouldReceiveTouch = shouldRecieve;
+ _panGesture.MaximumNumberOfTouches = 2;
+ View.AddGestureRecognizer(_panGesture);
+ }
+
+ class ChildViewController : UIViewController
+ {
+ public override void ViewDidLayoutSubviews()
+ {
+ foreach (var vc in ChildViewControllers)
+ vc.View.Frame = View.Bounds;
+ }
+ }
+ }
+} \ No newline at end of file