diff options
Diffstat (limited to 'Xamarin.Forms.Platform.iOS/Renderers/CarouselPageRenderer.cs')
-rw-r--r-- | Xamarin.Forms.Platform.iOS/Renderers/CarouselPageRenderer.cs | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.iOS/Renderers/CarouselPageRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/CarouselPageRenderer.cs new file mode 100644 index 00000000..16e97cd2 --- /dev/null +++ b/Xamarin.Forms.Platform.iOS/Renderers/CarouselPageRenderer.cs @@ -0,0 +1,395 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Drawing; +#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 CarouselPageRenderer : UIViewController, IVisualElementRenderer + { + bool _appeared; + Dictionary<Page, UIView> _containerMap; + bool _disposed; + EventTracker _events; + bool _ignoreNativeScrolling; + UIScrollView _scrollView; + VisualElementTracker _tracker; + + public CarouselPageRenderer() + { + if (!Forms.IsiOS7OrNewer) + WantsFullScreenLayout = true; + } + + protected CarouselPage Carousel + { + get { return (CarouselPage)Element; } + } + + protected int SelectedIndex + { + get { return (int)(_scrollView.ContentOffset.X / _scrollView.Frame.Width); } + set { ScrollToPage(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) + { + VisualElement oldElement = Element; + Element = element; + _containerMap = new Dictionary<Page, UIView>(); + + 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 DidRotate(UIInterfaceOrientation fromInterfaceOrientation) + { + _ignoreNativeScrolling = false; + View.SetNeedsLayout(); + } + + public override void ViewDidAppear(bool animated) + { + base.ViewDidAppear(animated); + + if (_appeared || _disposed) + return; + + _appeared = true; + Carousel.SendAppearing(); + } + + public override void ViewDidDisappear(bool animated) + { + base.ViewDidDisappear(animated); + + if (!_appeared || _disposed) + return; + + _appeared = false; + Carousel.SendDisappearing(); + } + + public override void ViewDidLayoutSubviews() + { + base.ViewDidLayoutSubviews(); + + View.Frame = View.Superview.Bounds; + _scrollView.Frame = View.Bounds; + + PositionChildren(); + UpdateCurrentPage(false); + } + + public override void ViewDidLoad() + { + base.ViewDidLoad(); + + _tracker = new VisualElementTracker(this); + _events = new EventTracker(this); + _events.LoadEvents(View); + + _scrollView = new UIScrollView { ShowsHorizontalScrollIndicator = false }; + + _scrollView.DecelerationEnded += OnDecelerationEnded; + + UpdateBackground(); + + View.Add(_scrollView); + + for (var i = 0; i < Element.LogicalChildren.Count; i++) + { + Element element = Element.LogicalChildren[i]; + var child = element as ContentPage; + if (child != null) + InsertPage(child, i); + } + + PositionChildren(); + + Carousel.PropertyChanged += OnPropertyChanged; + Carousel.PagesChanged += OnPagesChanged; + } + + public override void ViewDidUnload() + { + base.ViewDidUnload(); + + if (_scrollView != null) + _scrollView.DecelerationEnded -= OnDecelerationEnded; + + if (Carousel != null) + { + Carousel.PropertyChanged -= OnPropertyChanged; + Carousel.PagesChanged -= OnPagesChanged; + } + } + + public override void WillRotate(UIInterfaceOrientation toInterfaceOrientation, double duration) + { + _ignoreNativeScrolling = true; + } + + protected override void Dispose(bool disposing) + { + if (disposing && !_disposed) + { + if (_scrollView != null) + _scrollView.DecelerationEnded -= OnDecelerationEnded; + + if (Carousel != null) + { + Carousel.PropertyChanged -= OnPropertyChanged; + Carousel.PagesChanged -= OnPagesChanged; + } + + Platform.SetRenderer(Element, null); + + Clear(); + + if (_scrollView != null) + { + _scrollView.DecelerationEnded -= OnDecelerationEnded; + _scrollView.RemoveFromSuperview(); + _scrollView = null; + } + + if (_appeared) + { + _appeared = false; + Carousel.SendDisappearing(); + } + + if (_events != null) + { + _events.Dispose(); + _events = null; + } + + if (_tracker != null) + { + _tracker.Dispose(); + _tracker = null; + } + + Element = null; + _disposed = true; + } + + base.Dispose(disposing); + } + + protected virtual void OnElementChanged(VisualElementChangedEventArgs e) + { + EventHandler<VisualElementChangedEventArgs> changed = ElementChanged; + if (changed != null) + changed(this, e); + } + + void Clear() + { + foreach (KeyValuePair<Page, UIView> kvp in _containerMap) + { + kvp.Value.RemoveFromSuperview(); + IVisualElementRenderer renderer = Platform.GetRenderer(kvp.Key); + if (renderer != null) + { + renderer.ViewController.RemoveFromParentViewController(); + renderer.NativeView.RemoveFromSuperview(); + Platform.SetRenderer(kvp.Key, null); + } + } + _containerMap.Clear(); + } + + void InsertPage(ContentPage page, int index) + { + IVisualElementRenderer renderer = Platform.GetRenderer(page); + if (renderer == null) + { + renderer = Platform.CreateRenderer(page); + Platform.SetRenderer(page, renderer); + } + + UIView container = new PageContainer(page); + container.AddSubview(renderer.NativeView); + _containerMap[page] = container; + + AddChildViewController(renderer.ViewController); + _scrollView.InsertSubview(container, index); + + if ((index == 0 && SelectedIndex == 0) || (index < SelectedIndex)) + ScrollToPage(SelectedIndex + 1, false); + } + + void OnDecelerationEnded(object sender, EventArgs eventArgs) + { + if (_ignoreNativeScrolling || SelectedIndex >= Element.LogicalChildren.Count) + return; + + Carousel.CurrentPage = (ContentPage)Element.LogicalChildren[SelectedIndex]; + } + + void OnPagesChanged(object sender, NotifyCollectionChangedEventArgs e) + { + _ignoreNativeScrolling = true; + + NotifyCollectionChangedAction action = e.Apply((o, i, c) => InsertPage((ContentPage)o, i), (o, i) => RemovePage((ContentPage)o, i), Reset); + PositionChildren(); + + _ignoreNativeScrolling = false; + + if (action == NotifyCollectionChangedAction.Reset) + { + int index = Carousel.CurrentPage != null ? CarouselPage.GetIndex(Carousel.CurrentPage) : 0; + if (index < 0) + index = 0; + + ScrollToPage(index); + } + } + + void OnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "CurrentPage") + UpdateCurrentPage(); + else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName) + UpdateBackground(); + else if (e.PropertyName == Page.BackgroundImageProperty.PropertyName) + UpdateBackground(); + } + + void PositionChildren() + { + nfloat x = 0; + RectangleF bounds = View.Bounds; + foreach (ContentPage child in ((CarouselPage)Element).Children) + { + UIView container = _containerMap[child]; + + container.Frame = new RectangleF(x, bounds.Y, bounds.Width, bounds.Height); + x += bounds.Width; + } + + _scrollView.PagingEnabled = true; + _scrollView.ContentSize = new SizeF(bounds.Width * ((CarouselPage)Element).Children.Count, bounds.Height); + } + + void RemovePage(ContentPage page, int index) + { + UIView container = _containerMap[page]; + container.RemoveFromSuperview(); + _containerMap.Remove(page); + + IVisualElementRenderer renderer = Platform.GetRenderer(page); + if (renderer == null) + return; + + renderer.ViewController.RemoveFromParentViewController(); + renderer.NativeView.RemoveFromSuperview(); + } + + void Reset() + { + Clear(); + + for (var i = 0; i < Element.LogicalChildren.Count; i++) + { + Element element = Element.LogicalChildren[i]; + var child = element as ContentPage; + if (child != null) + InsertPage(child, i); + } + } + + void ScrollToPage(int index, bool animated = true) + { + if (_scrollView.ContentOffset.X == index * _scrollView.Frame.Width) + return; + + _scrollView.SetContentOffset(new PointF(index * _scrollView.Frame.Width, 0), animated); + } + + void UpdateBackground() + { + string bgImage = ((Page)Element).BackgroundImage; + if (!string.IsNullOrEmpty(bgImage)) + { + View.BackgroundColor = UIColor.FromPatternImage(UIImage.FromBundle(bgImage)); + return; + } + Color bgColor = Element.BackgroundColor; + if (bgColor.IsDefault) + View.BackgroundColor = UIColor.White; + else + View.BackgroundColor = bgColor.ToUIColor(); + } + + void UpdateCurrentPage(bool animated = true) + { + ContentPage current = Carousel.CurrentPage; + if (current != null) + ScrollToPage(CarouselPage.GetIndex(current), animated); + } + + class PageContainer : UIView + { + public PageContainer(VisualElement element) + { + Element = element; + } + + public VisualElement Element { get; } + + public override void LayoutSubviews() + { + base.LayoutSubviews(); + + if (Subviews.Length > 0) + Subviews[0].Frame = new RectangleF(0, 0, (float)Element.Width, (float)Element.Height); + } + } + } +}
\ No newline at end of file |