summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.iOS/Renderers/TabletMasterDetailRenderer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.iOS/Renderers/TabletMasterDetailRenderer.cs')
-rw-r--r--Xamarin.Forms.Platform.iOS/Renderers/TabletMasterDetailRenderer.cs371
1 files changed, 371 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.iOS/Renderers/TabletMasterDetailRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/TabletMasterDetailRenderer.cs
new file mode 100644
index 00000000..3561322c
--- /dev/null
+++ b/Xamarin.Forms.Platform.iOS/Renderers/TabletMasterDetailRenderer.cs
@@ -0,0 +1,371 @@
+using System;
+using System.ComponentModel;
+#if __UNIFIED__
+using UIKit;
+
+#else
+using MonoTouch.UIKit;
+#endif
+
+namespace Xamarin.Forms.Platform.iOS
+{
+ internal class ChildViewController : UIViewController
+ {
+ public override void ViewDidLayoutSubviews()
+ {
+ foreach (var vc in ChildViewControllers)
+ vc.View.Frame = View.Bounds;
+ }
+ }
+
+ internal class EventedViewController : ChildViewController
+ {
+ public override void ViewWillAppear(bool animated)
+ {
+ base.ViewWillAppear(animated);
+
+ var eh = WillAppear;
+ if (eh != null)
+ eh(this, EventArgs.Empty);
+ }
+
+ public override void ViewWillDisappear(bool animated)
+ {
+ base.ViewWillDisappear(animated);
+
+ var eh = WillDisappear;
+ if (eh != null)
+ eh(this, EventArgs.Empty);
+ }
+
+ public event EventHandler WillAppear;
+
+ public event EventHandler WillDisappear;
+ }
+
+ public class TabletMasterDetailRenderer : UISplitViewController, IVisualElementRenderer
+ {
+ UIViewController _detailController;
+
+ bool _disposed;
+ EventTracker _events;
+ InnerDelegate _innerDelegate;
+
+ EventedViewController _masterController;
+
+ MasterDetailPage _masterDetailPage;
+
+ bool _masterVisible;
+
+ VisualElementTracker _tracker;
+
+ protected MasterDetailPage MasterDetailPage
+ {
+ get { return _masterDetailPage ?? (_masterDetailPage = (MasterDetailPage)Element); }
+ }
+
+ UIBarButtonItem PresentButton
+ {
+ get { return _innerDelegate == null ? null : _innerDelegate.PresentButton; }
+ }
+
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+
+ if (Element != null)
+ {
+ ((Page)Element).SendDisappearing();
+ Element.PropertyChanged -= HandlePropertyChanged;
+ Element = null;
+ }
+
+ if (_tracker != null)
+ {
+ _tracker.Dispose();
+ _tracker = null;
+ }
+
+ if (_events != null)
+ {
+ _events.Dispose();
+ _events = null;
+ }
+
+ if (_masterController != null)
+ {
+ _masterController.WillAppear -= MasterControllerWillAppear;
+ _masterController.WillDisappear -= MasterControllerWillDisappear;
+ }
+
+ _disposed = true;
+ }
+
+ 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;
+
+ ViewControllers = new[] { _masterController = new EventedViewController(), _detailController = new ChildViewController() };
+
+ Delegate = _innerDelegate = new InnerDelegate(MasterDetailPage.MasterBehavior);
+
+ UpdateControllers();
+
+ _masterController.WillAppear += MasterControllerWillAppear;
+ _masterController.WillDisappear += MasterControllerWillDisappear;
+
+ PresentsWithGesture = MasterDetailPage.IsGestureEnabled;
+
+ OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
+
+ if (element != null)
+ element.SendViewInitialized(NativeView);
+ }
+
+ public void SetElementSize(Size size)
+ {
+ Element.Layout(new Rectangle(Element.X, Element.Width, size.Width, size.Height));
+ }
+
+ public UIViewController ViewController
+ {
+ get { return this; }
+ }
+
+ public override void ViewDidAppear(bool animated)
+ {
+ ((Page)Element).SendAppearing();
+ base.ViewDidAppear(animated);
+ ToggleMaster();
+ }
+
+ public override void ViewDidDisappear(bool animated)
+ {
+ base.ViewDidDisappear(animated);
+ ((Page)Element).SendDisappearing();
+ }
+
+ public override void ViewDidLayoutSubviews()
+ {
+ base.ViewDidLayoutSubviews();
+
+ if (View.Subviews.Length < 2)
+ return;
+
+ var detailsBounds = _detailController.View.Frame;
+ var masterBounds = _masterController.View.Frame;
+
+ if (!masterBounds.IsEmpty)
+ MasterDetailPage.MasterBounds = new Rectangle(0, 0, masterBounds.Width, masterBounds.Height);
+
+ if (!detailsBounds.IsEmpty)
+ MasterDetailPage.DetailBounds = new Rectangle(0, 0, detailsBounds.Width, detailsBounds.Height);
+ }
+
+ public override void ViewDidLoad()
+ {
+ base.ViewDidLoad();
+ UpdateBackground();
+ _tracker = new VisualElementTracker(this);
+ _events = new EventTracker(this);
+ _events.LoadEvents(NativeView);
+ }
+
+ public override void ViewWillDisappear(bool animated)
+ {
+ if (_masterVisible)
+ PerformButtonSelector();
+
+ base.ViewWillDisappear(animated);
+ }
+
+ public override void ViewWillLayoutSubviews()
+ {
+ base.ViewWillLayoutSubviews();
+ _masterController.View.BackgroundColor = UIColor.White;
+ }
+
+ public override void WillRotate(UIInterfaceOrientation toInterfaceOrientation, double duration)
+ {
+ // On IOS8 the MasterViewController ViewAppear/Disappear weren't being called correctly after rotation
+ // We now close the Master by using the new SplitView API, basicly we set it to hidden and right back to the Normal/AutomaticMode
+ if (!MasterDetailPage.ShouldShowSplitMode && _masterVisible)
+ {
+ MasterDetailPage.CanChangeIsPresented = true;
+ if (Forms.IsiOS8OrNewer)
+ {
+ PreferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryHidden;
+ PreferredDisplayMode = UISplitViewControllerDisplayMode.Automatic;
+ }
+ }
+ MasterDetailPage.UpdateMasterBehavior(MasterDetailPage);
+ MessagingCenter.Send<IVisualElementRenderer>(this, NavigationRenderer.UpdateToolbarButtons);
+ base.WillRotate(toInterfaceOrientation, duration);
+ }
+
+ protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
+ {
+ if (e.OldElement != null)
+ e.OldElement.PropertyChanged -= HandlePropertyChanged;
+
+ if (e.NewElement != null)
+ e.NewElement.PropertyChanged += HandlePropertyChanged;
+
+ var changed = ElementChanged;
+ if (changed != null)
+ changed(this, e);
+ }
+
+ void ClearControllers()
+ {
+ foreach (var controller in _masterController.ChildViewControllers)
+ {
+ controller.View.RemoveFromSuperview();
+ controller.RemoveFromParentViewController();
+ }
+
+ foreach (var controller in _detailController.ChildViewControllers)
+ {
+ controller.View.RemoveFromSuperview();
+ controller.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 (_tracker == null)
+ return;
+
+ if (e.PropertyName == "Master" || e.PropertyName == "Detail")
+ UpdateControllers();
+ else if (e.PropertyName == MasterDetailPage.IsPresentedProperty.PropertyName)
+ ToggleMaster();
+ else if (e.PropertyName == MasterDetailPage.IsGestureEnabledProperty.PropertyName)
+ PresentsWithGesture = MasterDetailPage.IsGestureEnabled;
+ MessagingCenter.Send<IVisualElementRenderer>(this, NavigationRenderer.UpdateToolbarButtons);
+ }
+
+ void MasterControllerWillAppear(object sender, EventArgs e)
+ {
+ _masterVisible = true;
+ if (MasterDetailPage.CanChangeIsPresented)
+ ((IElementController)Element).SetValueFromRenderer(MasterDetailPage.IsPresentedProperty, true);
+ }
+
+ void MasterControllerWillDisappear(object sender, EventArgs e)
+ {
+ _masterVisible = false;
+ if (MasterDetailPage.CanChangeIsPresented)
+ ((IElementController)Element).SetValueFromRenderer(MasterDetailPage.IsPresentedProperty, false);
+ }
+
+ void PerformButtonSelector()
+ {
+ if (Forms.IsiOS8OrNewer)
+ DisplayModeButtonItem.Target.PerformSelector(DisplayModeButtonItem.Action, DisplayModeButtonItem, 0);
+ else
+ PresentButton.Target.PerformSelector(PresentButton.Action, PresentButton, 0);
+ }
+
+ void ToggleMaster()
+ {
+ if (_masterVisible == MasterDetailPage.IsPresented || MasterDetailPage.ShouldShowSplitMode)
+ return;
+
+ PerformButtonSelector();
+ }
+
+ 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 UpdateControllers()
+ {
+ MasterDetailPage.Master.PropertyChanged -= HandleMasterPropertyChanged;
+
+ if (Platform.GetRenderer(MasterDetailPage.Master) == null)
+ Platform.SetRenderer(MasterDetailPage.Master, Platform.CreateRenderer(MasterDetailPage.Master));
+ if (Platform.GetRenderer(MasterDetailPage.Detail) == null)
+ Platform.SetRenderer(MasterDetailPage.Detail, Platform.CreateRenderer(MasterDetailPage.Detail));
+
+ ClearControllers();
+
+ MasterDetailPage.Master.PropertyChanged += HandleMasterPropertyChanged;
+
+ var master = Platform.GetRenderer(MasterDetailPage.Master).ViewController;
+ var detail = Platform.GetRenderer(MasterDetailPage.Detail).ViewController;
+
+ _masterController.View.AddSubview(master.View);
+ _masterController.AddChildViewController(master);
+
+ _detailController.View.AddSubview(detail.View);
+ _detailController.AddChildViewController(detail);
+ }
+
+ class InnerDelegate : UISplitViewControllerDelegate
+ {
+ readonly MasterBehavior _masterPresentedDefaultState;
+
+ public InnerDelegate(MasterBehavior masterPresentedDefaultState)
+ {
+ _masterPresentedDefaultState = masterPresentedDefaultState;
+ }
+
+ public UIBarButtonItem PresentButton { get; set; }
+
+ public override bool ShouldHideViewController(UISplitViewController svc, UIViewController viewController, UIInterfaceOrientation inOrientation)
+ {
+ bool willHideViewController;
+ switch (_masterPresentedDefaultState)
+ {
+ case MasterBehavior.Split:
+ willHideViewController = false;
+ break;
+ case MasterBehavior.Popover:
+ willHideViewController = true;
+ break;
+ case MasterBehavior.SplitOnPortrait:
+ willHideViewController = !(inOrientation == UIInterfaceOrientation.Portrait || inOrientation == UIInterfaceOrientation.PortraitUpsideDown);
+ break;
+ default:
+ willHideViewController = inOrientation == UIInterfaceOrientation.Portrait || inOrientation == UIInterfaceOrientation.PortraitUpsideDown;
+ break;
+ }
+ return willHideViewController;
+ }
+
+ public override void WillHideViewController(UISplitViewController svc, UIViewController aViewController, UIBarButtonItem barButtonItem, UIPopoverController pc)
+ {
+ PresentButton = barButtonItem;
+ }
+ }
+ }
+} \ No newline at end of file