--- /dev/null
+<?xml version="1.0" encoding="utf-8" ?>
+<controls:TestContentPage
+ xmlns="http://xamarin.com/schemas/2014/forms"
+ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+ xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
+ x:Class="Xamarin.Forms.Controls.Issues.Github5623">
+ <Grid>
+ <Grid.RowDefinitions>
+ <RowDefinition Height="200"/>
+ <RowDefinition/>
+ </Grid.RowDefinitions>
+
+ <StackLayout Orientation="Vertical" Spacing="5" Grid.Row="0" VerticalOptions="Center">
+ <Label x:Name="Label" LineBreakMode="WordWrap" Text="Scroll down until you hit 99" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+ <Label x:Name="Label1" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+ <Label x:Name="Label2" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+ <Label x:Name="Label3" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+ <Label x:Name="Label4" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+ <Label x:Name="Label5" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+ <Label x:Name="Label6" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+ <Label x:Name="Label7" LineBreakMode="WordWrap" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+ </StackLayout>
+
+ <CollectionView Grid.Row="1" AutomationId="CollectionView5623" ItemSizingStrategy="{Binding ItemSizingStrategy}" ItemsSource="{Binding Items}" Scrolled="CollectionView_OnScrolled" RemainingItemsThreshold="25" RemainingItemsThresholdReached="CollectionView_RemainingItemsThresholdReached" RemainingItemsThresholdReachedCommand="{Binding RemainingItemsThresholdReachedCommand}">
+ <CollectionView.ItemTemplate>
+ <DataTemplate>
+ <Grid HeightRequest="{Binding Height}" BackgroundColor="{Binding BackgroundColor}">
+ <StackLayout Spacing="10" HorizontalOptions="Center" VerticalOptions="Center">
+ <Label Text="{Binding Text}" HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+ <Label Text="{Binding HeightText}" FontSize="Micro" HorizontalTextAlignment="Center" VerticalTextAlignment="End"/>
+ </StackLayout>
+ </Grid>
+ </DataTemplate>
+ </CollectionView.ItemTemplate>
+ </CollectionView>
+
+ <BoxView Grid.Row="1" HorizontalOptions="FillAndExpand" VerticalOptions="Center" BackgroundColor="Red" HeightRequest="5"/>
+ </Grid>
+</controls:TestContentPage>
\ No newline at end of file
--- /dev/null
+using System.Collections.ObjectModel;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+using System;
+using System.Security.Cryptography;
+using Xamarin.Forms.Xaml;
+using System.Threading;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+#if UITEST
+using Xamarin.UITest;
+using Xamarin.UITest.Queries;
+using NUnit.Framework;
+using Xamarin.Forms.Core.UITests;
+using System.Linq;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+ [Category(UITestCategories.CollectionView)]
+#endif
+#if APP
+ [XamlCompilation(XamlCompilationOptions.Compile)]
+#endif
+ [Preserve(AllMembers = true)]
+ [Issue(IssueTracker.Github, 5623, "CollectionView with Incremental Collection (RemainingItemsThreshold)", PlatformAffected.All)]
+ public partial class Github5623 : TestContentPage
+ {
+#if APP
+ int _itemCount = 10;
+ const int MaximumItemCount = 100;
+ const int PageSize = 10;
+ static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1);
+
+ public Github5623()
+ {
+ Device.SetFlags(new List<string> { CollectionView.CollectionViewExperimental });
+
+ InitializeComponent();
+
+ BindingContext = new ViewModel5623();
+ }
+
+ async void CollectionView_RemainingItemsThresholdReached(object sender, System.EventArgs e)
+ {
+ await SemaphoreSlim.WaitAsync();
+ try
+ {
+ var itemsSource = (sender as CollectionView).ItemsSource as ObservableCollection<Model5623>;
+ var nextSet = await GetNextSetAsync();
+
+ // nothing to add
+ if (nextSet.Count == 0)
+ return;
+
+ Device.BeginInvokeOnMainThread(() =>
+ {
+ foreach (var item in nextSet)
+ {
+ itemsSource.Add(item);
+ }
+ });
+
+ System.Diagnostics.Debug.WriteLine("Count: " + itemsSource.Count);
+ }
+ finally
+ {
+ SemaphoreSlim.Release();
+ }
+ }
+
+ void CollectionView_OnScrolled(object sender, ItemsViewScrolledEventArgs e)
+ {
+ Label1.Text = "HorizontalDelta: " + e.HorizontalDelta;
+ Label2.Text = "VerticalDelta: " + e.VerticalDelta;
+ Label3.Text = "HorizontalOffset: " + e.HorizontalOffset;
+ Label4.Text = "VerticalOffset: " + e.VerticalOffset;
+ Label5.Text = "FirstVisibleItemIndex: " + e.FirstVisibleItemIndex;
+ Label6.Text = "CenterItemIndex: " + e.CenterItemIndex;
+ Label7.Text = "LastVisibleItemIndex: " + e.LastVisibleItemIndex;
+ }
+
+ async Task<ObservableCollection<Model5623>> GetNextSetAsync()
+ {
+ return await Task.Run(() =>
+ {
+ var collection = new ObservableCollection<Model5623>();
+ var count = PageSize;
+
+ if (_itemCount + count > MaximumItemCount)
+ count = MaximumItemCount - _itemCount;
+
+ for (var i = _itemCount; i < _itemCount + count; i++)
+ {
+ collection.Add(new Model5623((BindingContext as ViewModel5623).ItemSizingStrategy == ItemSizingStrategy.MeasureAllItems)
+ {
+ Text = i.ToString(),
+ BackgroundColor = i % 2 == 0 ? Color.AntiqueWhite : Color.Lavender
+ });
+ }
+
+ _itemCount += count;
+
+ return collection;
+ });
+ }
+#endif
+
+ protected override void Init()
+ {
+
+ }
+
+#if UITEST
+ [Test]
+ public void CollectionViewInfiniteScroll()
+ {
+ RunningApp.WaitForElement ("CollectionView5623");
+
+ var colView = RunningApp.Query("CollectionView5623").Single();
+
+ AppResult[] lastCellResults = null;
+
+ RunningApp.RetryUntilPresent(() =>
+ {
+ RunningApp.DragCoordinates(colView.Rect.CenterX, colView.Rect.Y + colView.Rect.Height - 50, colView.Rect.CenterX, colView.Rect.Y + 5);
+
+ lastCellResults = RunningApp.Query("99");
+
+ return lastCellResults;
+ }, 100, 1);
+
+ Assert.IsTrue(lastCellResults?.Any() ?? false);
+ }
+#endif
+ }
+
+ [Preserve(AllMembers = true)]
+ public class ViewModel5623
+ {
+ public ObservableCollection<Model5623> Items { get; set; }
+
+ public Command RemainingItemsThresholdReachedCommand { get; set; }
+
+ public ItemSizingStrategy ItemSizingStrategy { get; set; } = ItemSizingStrategy.MeasureAllItems;
+
+ public ViewModel5623()
+ {
+ var collection = new ObservableCollection<Model5623>();
+ var pageSize = 10;
+
+ for (var i = 0; i < pageSize; i++)
+ {
+ collection.Add(new Model5623(ItemSizingStrategy == ItemSizingStrategy.MeasureAllItems)
+ {
+ Text = i.ToString(),
+ BackgroundColor = i % 2 == 0 ? Color.AntiqueWhite : Color.Lavender
+ });
+ }
+
+ Items = collection;
+
+ RemainingItemsThresholdReachedCommand = new Command(() =>
+ {
+ System.Diagnostics.Debug.WriteLine($"{nameof(RemainingItemsThresholdReachedCommand)} called");
+ });
+ }
+ }
+
+ [Preserve(AllMembers = true)]
+ public class Model5623
+ {
+ RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider();
+
+ public string Text { get; set; }
+
+ public Color BackgroundColor { get; set; }
+
+ public int Height { get; set; } = 200;
+
+ public string HeightText { get; private set; }
+
+ public Model5623(bool isUneven)
+ {
+ var byteArray = new byte[4];
+ provider.GetBytes(byteArray);
+
+ if (isUneven)
+ Height = 100 + (BitConverter.ToInt32(byteArray, 0) % 300 + 300) % 300;
+
+ HeightText = "(Height: " + Height + ")";
+ }
+ }
+}
\ No newline at end of file
<Compile Include="$(MSBuildThisFileDirectory)Issue6258.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue3150.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue6262.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)Github5623.xaml.cs">
+ <DependentUpon>Github5623.xaml</DependentUpon>
+ <SubType>Code</SubType>
+ </Compile>
<Compile Include="$(MSBuildThisFileDirectory)Bugzilla59172.cs" />
<Compile Include="$(MSBuildThisFileDirectory)FlagTestHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue6260.cs" />
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue4600.cs" />
- <Compile Include="$(MSBuildThisFileDirectory)Issue5252.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5057.xaml.cs">
<DependentUpon>Issue5057.xaml</DependentUpon>
<SubType>Code</SubType>
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
</ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="$(MSBuildThisFileDirectory)Github5623.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </EmbeddedResource>
+ </ItemGroup>
</Project>
\ No newline at end of file
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.Diagnostics;
-using System.Text.RegularExpressions;
+using System.Windows.Input;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms
set => SetValue(ItemsSourceProperty, value);
}
+ public static readonly BindableProperty RemainingItemsThresholdReachedCommandProperty =
+ BindableProperty.Create(nameof(RemainingItemsThresholdReachedCommand), typeof(ICommand), typeof(ItemsView), null);
+
+ public ICommand RemainingItemsThresholdReachedCommand
+ {
+ get => (ICommand)GetValue(RemainingItemsThresholdReachedCommandProperty);
+ set => SetValue(RemainingItemsThresholdReachedCommandProperty, value);
+ }
+
+ public static readonly BindableProperty RemainingItemsThresholdReachedCommandParameterProperty = BindableProperty.Create(nameof(RemainingItemsThresholdReachedCommandParameter), typeof(object), typeof(ItemsView), default(object));
+
+ public object RemainingItemsThresholdReachedCommandParameter
+ {
+ get => GetValue(RemainingItemsThresholdReachedCommandParameterProperty);
+ set => SetValue(RemainingItemsThresholdReachedCommandParameterProperty, value);
+ }
+
public static readonly BindableProperty HorizontalScrollBarVisibilityProperty = BindableProperty.Create(
nameof(HorizontalScrollBarVisibility),
typeof(ScrollBarVisibility),
set => SetValue(VerticalScrollBarVisibilityProperty, value);
}
+ public static readonly BindableProperty RemainingItemsThresholdProperty =
+ BindableProperty.Create(nameof(RemainingItemsThreshold), typeof(int), typeof(ItemsView), -1, validateValue: (bindable, value) => (int)value >= -1);
+
+ public int RemainingItemsThreshold
+ {
+ get => (int)GetValue(RemainingItemsThresholdProperty);
+ set => SetValue(RemainingItemsThresholdProperty, value);
+ }
+
public void AddLogicalChild(Element element)
{
_logicalChildren.Add(element);
OnScrollToRequested(new ScrollToRequestEventArgs(item, group, position, animate));
}
+ public void SendRemainingItemsThresholdReached()
+ {
+ RemainingItemsThresholdReached?.Invoke(this, EventArgs.Empty);
+
+ if (RemainingItemsThresholdReachedCommand?.CanExecute(RemainingItemsThresholdReachedCommandParameter) == true)
+ RemainingItemsThresholdReachedCommand?.Execute(RemainingItemsThresholdReachedCommandParameter);
+
+ OnRemainingItemsThresholdReached();
+ }
+
+ public void SendScrolled(ItemsViewScrolledEventArgs e)
+ {
+ Scrolled?.Invoke(this, e);
+
+ OnScrolled(e);
+ }
+
public event EventHandler<ScrollToRequestEventArgs> ScrollToRequested;
+ public event EventHandler<ItemsViewScrolledEventArgs> Scrolled;
+
+ public event EventHandler RemainingItemsThresholdReached;
+
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
// TODO hartez 2018-05-22 05:04 PM This 40,40 is what LV1 does; can we come up with something less arbitrary?
{
ScrollToRequested?.Invoke(this, e);
}
+
+ protected virtual void OnRemainingItemsThresholdReached()
+ {
+
+ }
+
+ protected virtual void OnScrolled(ItemsViewScrolledEventArgs e)
+ {
+
+ }
}
}
\ No newline at end of file
--- /dev/null
+using System;
+
+namespace Xamarin.Forms
+{
+ public class ItemsViewScrolledEventArgs : EventArgs
+ {
+ public double HorizontalDelta { get; set; }
+
+ public double VerticalDelta { get; set; }
+
+ public double HorizontalOffset { get; set; }
+
+ public double VerticalOffset { get; set; }
+
+ public int FirstVisibleItemIndex { get; set; }
+
+ public int CenterItemIndex { get; set; }
+
+ public int LastVisibleItemIndex { get; set; }
+ }
+}
\ No newline at end of file
public EmptyViewAdapter(ItemsView itemsView)
{
- CollectionView.VerifyCollectionViewFlagEnabled(nameof(EmptyViewAdapter));
+ Xamarin.Forms.CollectionView.VerifyCollectionViewFlagEnabled(nameof(EmptyViewAdapter));
ItemsView = itemsView;
}
internal ItemsViewAdapter(ItemsView itemsView, Func<View, Context, ItemContentView> createItemContentView = null)
{
- CollectionView.VerifyCollectionViewFlagEnabled(nameof(ItemsViewAdapter));
+ Xamarin.Forms.CollectionView.VerifyCollectionViewFlagEnabled(nameof(ItemsViewAdapter));
ItemsView = itemsView;
_createItemContentView = createItemContentView;
using System;
using System.ComponentModel;
-using System.Linq;
using Android.Content;
using Android.Graphics;
using Android.Support.V7.Widget;
-using Android.Util;
using Android.Views;
-using Android.Widget;
using Xamarin.Forms.Internals;
+using Xamarin.Forms.Platform.Android.CollectionView;
using Xamarin.Forms.Platform.Android.FastRenderers;
using AViewCompat = Android.Support.V4.View.ViewCompat;
IItemsLayout _layout;
SnapManager _snapManager;
ScrollHelper _scrollHelper;
+ RecyclerViewScrollListener _recyclerViewScrollListener;
EmptyViewAdapter _emptyViewAdapter;
readonly DataChangeObserver _emptyCollectionObserver;
public ItemsViewRenderer(Context context) : base(new ContextThemeWrapper(context, Resource.Style.collectionViewStyle))
{
- CollectionView.VerifyCollectionViewFlagEnabled(nameof(ItemsViewRenderer));
+ Xamarin.Forms.CollectionView.VerifyCollectionViewFlagEnabled(nameof(ItemsViewRenderer));
_automationPropertiesProvider = new AutomationPropertiesProvider(this);
_effectControlProvider = new EffectControlProvider(this);
// Listen for ScrollTo requests
ItemsView.ScrollToRequested += ScrollToRequested;
+
+ _recyclerViewScrollListener = new RecyclerViewScrollListener(ItemsView, ItemsViewAdapter);
+ AddOnScrollListener(_recyclerViewScrollListener);
}
void UpdateVerticalScrollBarVisibility()
// Stop listening for ScrollTo requests
oldElement.ScrollToRequested -= ScrollToRequested;
+ if (_recyclerViewScrollListener != null)
+ {
+ _recyclerViewScrollListener.Dispose();
+ ClearOnScrollListeners();
+ _recyclerViewScrollListener = null;
+ }
+
if (ItemsViewAdapter != null)
{
// Stop watching for empty items or scroll adjustments
public PositionalSmoothScroller(Context context, ScrollToPosition scrollToPosition) : base(context)
{
- CollectionView.VerifyCollectionViewFlagEnabled(nameof(PositionalSmoothScroller));
+ Xamarin.Forms.CollectionView.VerifyCollectionViewFlagEnabled(nameof(PositionalSmoothScroller));
_scrollToPosition = scrollToPosition;
}
--- /dev/null
+using System.Collections.Generic;
+using System.Linq;
+using Android.Graphics;
+using Android.Support.V7.Widget;
+
+namespace Xamarin.Forms.Platform.Android.CollectionView
+{
+ public class RecyclerViewScrollListener : RecyclerView.OnScrollListener
+ {
+ bool _disposed;
+ int _horizontalOffset, _verticalOffset;
+ ItemsView _itemsView;
+ ItemsViewAdapter _itemsViewAdapter;
+
+ public RecyclerViewScrollListener(ItemsView itemsView, ItemsViewAdapter itemsViewAdapter)
+ {
+ _itemsView = itemsView;
+ _itemsViewAdapter = itemsViewAdapter;
+ }
+
+ public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
+ {
+ base.OnScrolled(recyclerView, dx, dy);
+
+ // TODO: These offsets will be incorrect upon row size or count change.
+ // They are currently provided in place of LayoutManager's default offset calculation
+ // because it does not report accurate values in the presence of uneven rows.
+ // See https://stackoverflow.com/questions/27507715/android-how-to-get-the-current-x-offset-of-recyclerview
+ _horizontalOffset += dx;
+ _verticalOffset += dy;
+
+ var firstVisibleItemIndex = -1;
+ var lastVisibleItemIndex = -1;
+ var centerItemIndex = -1;
+
+ if (recyclerView.GetLayoutManager() is LinearLayoutManager linearLayoutManager)
+ {
+ firstVisibleItemIndex = linearLayoutManager.FindFirstVisibleItemPosition();
+ lastVisibleItemIndex = linearLayoutManager.FindLastVisibleItemPosition();
+ centerItemIndex = CalculateCenterItemIndex(firstVisibleItemIndex, lastVisibleItemIndex, linearLayoutManager);
+ }
+
+ var itemsViewScrolledEventArgs = new ItemsViewScrolledEventArgs
+ {
+ HorizontalDelta = dx,
+ VerticalDelta = dy,
+ HorizontalOffset = _horizontalOffset,
+ VerticalOffset = _verticalOffset,
+ FirstVisibleItemIndex = firstVisibleItemIndex,
+ CenterItemIndex = centerItemIndex,
+ LastVisibleItemIndex = lastVisibleItemIndex
+ };
+
+ _itemsView.SendScrolled(itemsViewScrolledEventArgs);
+
+ // Don't send RemainingItemsThresholdReached event for non-linear layout managers
+ // This can also happen if a layout pass has not happened yet
+ if (lastVisibleItemIndex == -1)
+ return;
+
+ switch (_itemsView.RemainingItemsThreshold)
+ {
+ case -1:
+ return;
+ case 0:
+ if (lastVisibleItemIndex == _itemsViewAdapter.ItemCount - 1)
+ _itemsView.SendRemainingItemsThresholdReached();
+ break;
+ default:
+ if (_itemsViewAdapter.ItemCount - 1 - lastVisibleItemIndex <= _itemsView.RemainingItemsThreshold)
+ _itemsView.SendRemainingItemsThresholdReached();
+ break;
+ }
+ }
+
+ static int CalculateCenterItemIndex(int firstVisibleItemIndex, int lastVisibleItemIndex, LinearLayoutManager linearLayoutManager)
+ {
+ // This can happen if a layout pass has not happened yet
+ if (firstVisibleItemIndex == -1)
+ return firstVisibleItemIndex;
+
+ var keyValuePairs = new Dictionary<int, int>();
+ for (var i = firstVisibleItemIndex; i <= lastVisibleItemIndex; i++)
+ {
+ var view = linearLayoutManager.FindViewByPosition(i);
+ var rect = new Rect();
+
+ view.GetLocalVisibleRect(rect);
+ keyValuePairs[i] = rect.Height();
+ }
+
+ var center = keyValuePairs.Values.Sum() / 2.0;
+ foreach (var keyValuePair in keyValuePairs)
+ {
+ center -= keyValuePair.Value;
+
+ if (center <= 0)
+ return keyValuePair.Key;
+ }
+
+ return firstVisibleItemIndex;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ if (disposing)
+ {
+ _itemsView = null;
+ _itemsViewAdapter = null;
+ }
+
+ _disposed = true;
+
+ base.Dispose(disposing);
+ }
+ }
+}
\ No newline at end of file
public SnapManager(ItemsView itemsView, RecyclerView recyclerView)
{
- CollectionView.VerifyCollectionViewFlagEnabled(nameof(SnapManager));
+ Xamarin.Forms.CollectionView.VerifyCollectionViewFlagEnabled(nameof(SnapManager));
_recyclerView = recyclerView;
_itemsView = itemsView;
}
<Compile Include="CollectionView\DataChangeObserver.cs" />
<Compile Include="CollectionView\EmptySource.cs" />
<Compile Include="CollectionView\NongreedySnapHelper.cs" />
+ <Compile Include="CollectionView\RecyclerViewScrollListener.cs" />
<Compile Include="CollectionView\SingleSnapHelper.cs" />
<Compile Include="CollectionView\EmptyViewAdapter.cs" />
<Compile Include="CollectionView\EndSingleSnapHelper.cs" />
// TODO hartez 2018/06/01 14:21:24 Add a method for updating the layout
public class ItemsViewController : UICollectionViewController
{
- protected IItemsViewSource ItemsSource { get; set; }
+ public IItemsViewSource ItemsSource { get; protected set; }
public ItemsView ItemsView { get; }
protected ItemsViewLayout ItemsViewLayout { get; set; }
bool _initialConstraintsSet;
using System;
+using System.Linq;
using CoreGraphics;
using Foundation;
using UIKit;
{
public class UICollectionViewDelegator : UICollectionViewDelegateFlowLayout
{
- public ItemsViewLayout ItemsViewLayout { get; private set; }
- public ItemsViewController ItemsViewController { get; private set; }
- public SelectableItemsViewController SelectableItemsViewController
+ float _previousHorizontalOffset, _previousVerticalOffset;
+
+ public ItemsViewLayout ItemsViewLayout { get; }
+ public ItemsViewController ItemsViewController { get; }
+ public SelectableItemsViewController SelectableItemsViewController => ItemsViewController as SelectableItemsViewController;
+
+ public GroupableItemsViewController GroupableItemsViewController => ItemsViewController as GroupableItemsViewController;
+
+ public UICollectionViewDelegator(ItemsViewLayout itemsViewLayout, ItemsViewController itemsViewController)
{
- get => ItemsViewController as SelectableItemsViewController;
+ ItemsViewLayout = itemsViewLayout;
+ ItemsViewController = itemsViewController;
}
- public GroupableItemsViewController GroupableItemsViewController
+ public override void DraggingStarted(UIScrollView scrollView)
{
- get => ItemsViewController as GroupableItemsViewController;
+ _previousHorizontalOffset = (float)scrollView.ContentOffset.X;
+ _previousVerticalOffset = (float)scrollView.ContentOffset.Y;
}
- public UICollectionViewDelegator(ItemsViewLayout itemsViewLayout, ItemsViewController itemsViewController)
+ public override void DraggingEnded(UIScrollView scrollView, bool willDecelerate)
{
- ItemsViewLayout = itemsViewLayout;
- ItemsViewController = itemsViewController;
+ _previousHorizontalOffset = 0;
+ _previousVerticalOffset = 0;
+ }
+
+ public override void Scrolled(UIScrollView scrollView)
+ {
+ var indexPathsForVisibleItems = ItemsViewController.CollectionView.IndexPathsForVisibleItems.OrderBy(x => x.Row).ToList();
+
+ if (indexPathsForVisibleItems.Count == 0)
+ return;
+
+ var firstVisibleItemIndex = (int)indexPathsForVisibleItems.First().Item;
+ var centerPoint = new CGPoint(ItemsViewController.CollectionView.Center.X + ItemsViewController.CollectionView.ContentOffset.X, ItemsViewController.CollectionView.Center.Y + ItemsViewController.CollectionView.ContentOffset.Y);
+ var centerIndexPath = ItemsViewController.CollectionView.IndexPathForItemAtPoint(centerPoint);
+ var centerItemIndex = centerIndexPath?.Row ?? firstVisibleItemIndex;
+ var lastVisibleItemIndex = (int)indexPathsForVisibleItems.Last().Item;
+ var itemsViewScrolledEventArgs = new ItemsViewScrolledEventArgs
+ {
+ HorizontalDelta = scrollView.ContentOffset.X - _previousHorizontalOffset,
+ VerticalDelta = scrollView.ContentOffset.Y - _previousVerticalOffset,
+ HorizontalOffset = scrollView.ContentOffset.X,
+ VerticalOffset = scrollView.ContentOffset.Y,
+ FirstVisibleItemIndex = firstVisibleItemIndex,
+ CenterItemIndex = centerItemIndex,
+ LastVisibleItemIndex = lastVisibleItemIndex
+ };
+
+ ItemsViewController.ItemsView.SendScrolled(itemsViewScrolledEventArgs);
+
+ _previousHorizontalOffset = (float)scrollView.ContentOffset.X;
+ _previousVerticalOffset = (float)scrollView.ContentOffset.Y;
+
+ switch (ItemsViewController.ItemsView.RemainingItemsThreshold)
+ {
+ case -1:
+ return;
+ case 0:
+ if (lastVisibleItemIndex == ItemsViewController.ItemsSource.ItemCount - 1)
+ ItemsViewController.ItemsView.SendRemainingItemsThresholdReached();
+ break;
+ default:
+ if (ItemsViewController.ItemsSource.ItemCount - 1 - lastVisibleItemIndex <= ItemsViewController.ItemsView.RemainingItemsThreshold)
+ ItemsViewController.ItemsView.SendRemainingItemsThresholdReached();
+ break;
+ }
}
public override UIEdgeInsets GetInsetForSection(UICollectionView collectionView, UICollectionViewLayout layout,