Skip to content

Instantly share code, notes, and snippets.

@paul-kiar
Last active November 8, 2018 20:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save paul-kiar/4ca352a3add70d548742291441dc9873 to your computer and use it in GitHub Desktop.
Save paul-kiar/4ca352a3add70d548742291441dc9873 to your computer and use it in GitHub Desktop.
CarouselViewRendererFix for iOS
using CoreGraphics;
using System;
using System.Reflection;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(CarouselView), typeof(YourNameSpace.iOS.Renderers.CarouselViewRendererFix))]
namespace YourNameSpace.iOS.Renderers
{
// this is an implementation of this pull request: https://github.com/xamarin/Xamarin.Forms.CarouselView/pull/13/files
public class CarouselViewRendererFix : Xamarin.Forms.Platform.CarouselViewRenderer
{
private CGRect? _lastBounds;
private UICollectionViewController _controller = null;
private MethodInfo _controllerReloadDataMethod = null;
private MethodInfo _controllerScrollToPositionMethod = null;
private Type _controllerType = null;
/// <summary>
/// The underlying controller of the view
/// </summary>
protected UICollectionViewController Controller
{
get
{
if (_controller == null)
{
FieldInfo fi = typeof(Xamarin.Forms.Platform.CarouselViewRenderer).GetField(nameof(_controller), BindingFlags.NonPublic | BindingFlags.Instance);
object cvc = fi?.GetValue(this);
_controller = (UICollectionViewController)cvc;
}
return _controller;
}
}
/// <summary>
/// The underlying controller has an internal type, so I have to get it by reflection
/// </summary>
protected Type ControllerType
{
get
{
if (_controllerType == null)
{
_controllerType = Controller?.GetType();
}
return _controllerType;
}
}
/// <summary>
/// The method that reloads the data on the Controller, it is an internal method so I have to get it by reflection
/// </summary>
protected MethodInfo ControllerReloadDataMethod
{
get
{
if (_controllerReloadDataMethod == null)
{
_controllerReloadDataMethod = ControllerType?.GetMethod(nameof(ReloadData), BindingFlags.Instance | BindingFlags.NonPublic);
}
return _controllerReloadDataMethod;
}
}
/// <summary>
/// The method that scrolls to position on the Controller, it is an internal method so I have to get it by reflection
/// </summary>
protected MethodInfo ControllerScrollToPositionMethod
{
get
{
if (_controllerScrollToPositionMethod == null)
{
_controllerScrollToPositionMethod = ControllerType?.GetMethod(nameof(ScrollToPosition), BindingFlags.Instance | BindingFlags.NonPublic);
}
return _controllerScrollToPositionMethod;
}
}
/// <summary>
/// Reload the data in the controller
/// </summary>
/// <param name="position">current position in the data</param>
protected void ReloadData(int position)
{
var parameters = new object[1];
parameters[0] = position;
ControllerReloadDataMethod?.Invoke(Controller, parameters);
}
/// <summary>
/// Scroll to a position on the controller
/// </summary>
/// <param name="position">position to scroll to</param>
/// <param name="animate">show animations when scrolling</param>
protected void ScrollToPosition(int position, bool animate)
{
var parameters = new object[2];
parameters[0] = position;
parameters[1] = animate;
ControllerScrollToPositionMethod?.Invoke(Controller, parameters);
}
/// <summary>
/// This override will adjust the layout on rotation (code in the base implementation doesn't work)
/// </summary>
public override void LayoutSubviews()
{
bool? wasPortrait = null;
if ( _lastBounds != null )
{
wasPortrait = _lastBounds.Value.Height > _lastBounds.Value.Width;
}
base.LayoutSubviews();
var nowPortrait = Bounds.Height > Bounds.Width;
if ( wasPortrait == null || wasPortrait != nowPortrait )
{
ScrollToPosition(Element.Position, false);
}
_lastBounds = Bounds;
}
/// <summary>
/// This forces reload of the data when the Element Changes
/// </summary>
/// <param name="e"></param>
protected override void OnElementChanged(ElementChangedEventArgs<CarouselView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
ReloadData(Element.Position);
}
}
/// <summary>
/// Cleans up the controller and control
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose(bool disposing)
{
var control = Control;
base.Dispose(disposing);
if ( disposing )
{
if( Controller != null)
{
Controller.Dispose();
}
if (control != null)
{
control.Dispose();
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment