[iOS] CollectionView multi-item Add/Remove/Replace/Move (#5055)
authorE.Z. Hart <hartez@users.noreply.github.com>
Fri, 8 Feb 2019 12:18:44 +0000 (05:18 -0700)
committerRui Marinho <me@ruimarinho.net>
Fri, 8 Feb 2019 12:18:44 +0000 (12:18 +0000)
* Split file by class

* Handle multiple moving items on iOS

* Make replacer indexes more intuitive

* Handle multi-item replacement on iOS

* More robust Remove implementation

* Make Replace test indexes include the end index; don't determine startIndex when removing
unless necessary.

Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/MultiItemRemover.cs
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/MultiItemReplacer.cs
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ObservableCollectionResetGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ObservableMultiItemCollectionViewGallery.cs
Xamarin.Forms.Platform.iOS/CollectionView/ObservableItemsSource.cs

index 6b7fe6c..53a4a2a 100644 (file)
                        var index1 = indexes[0];
                        var index2 = indexes[1];
 
-                       if (index1 > -1 && index2 > -1 && index1 < observableCollection.Count &&
-                               index2 < observableCollection.Count && index1 < index2)
+                       if (index1 > -1 && index2 < observableCollection.Count && index1 <= index2)
                        {
                                if (_withIndex)
                                {
-                                       observableCollection.TestRemoveWithListAndIndex(index1, index2 - index1);
+                                       observableCollection.TestRemoveWithListAndIndex(index1, (index2 - index1) + 1);
                                }
                                else
                                {
-                                       observableCollection.TestRemoveWithList(index1, index2 - index1);
+                                       observableCollection.TestRemoveWithList(index1, (index2 - index1) + 1);
                                }
                        }
                }
index 4a55be2..64a0cc8 100644 (file)
@@ -32,8 +32,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                        var index1 = indexes[0];
                        var index2 = indexes[1];
 
-                       if (index1 > -1 && index2 > -1 && index1 < observableCollection.Count &&
-                               index2 < observableCollection.Count && index1 < index2)
+                       if (index1 > -1 && index2 < observableCollection.Count && index1 <= index2)
                        {
                                var newItems = new List<CollectionViewGalleryTestItem>();
 
@@ -45,11 +44,11 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
 
                                if (_withIndex)
                                {
-                                       observableCollection.TestReplaceWithListAndIndex(index1, index2 - index1, newItems);
+                                       observableCollection.TestReplaceWithListAndIndex(index1, index2 - index1 + 1, newItems);
                                }
                                else
                                {
-                                       observableCollection.TestReplaceWithList(index1, index2 - index1, newItems);
+                                       observableCollection.TestReplaceWithList(index1, index2 - index1 + 1, newItems);
                                }
                        }
                }
diff --git a/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ObservableCollectionResetGallery.cs b/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ObservableCollectionResetGallery.cs
new file mode 100644 (file)
index 0000000..5cbd401
--- /dev/null
@@ -0,0 +1,39 @@
+namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
+{
+       internal class ObservableCollectionResetGallery : ContentPage
+       {
+               public ObservableCollectionResetGallery()
+               {
+                       var layout = new Grid
+                       {
+                               RowDefinitions = new RowDefinitionCollection
+                               {
+                                       new RowDefinition { Height = GridLength.Auto },
+                                       new RowDefinition { Height = GridLength.Auto },
+                                       new RowDefinition { Height = GridLength.Star }
+                               }
+                       };
+
+                       var itemsLayout = new GridItemsLayout(3, ItemsLayoutOrientation.Vertical) as IItemsLayout;
+
+                       var itemTemplate = ExampleTemplates.PhotoTemplate();
+
+                       var collectionView = new CollectionView { ItemsLayout = itemsLayout, ItemTemplate = itemTemplate };
+
+                       var generator = new ItemsSourceGenerator(collectionView, 100, ItemsSourceType.MultiTestObservableCollection);
+
+                       layout.Children.Add(generator);
+
+                       var resetter = new Resetter(collectionView);
+                       layout.Children.Add(resetter);
+                       Grid.SetRow(resetter, 1);
+
+                       layout.Children.Add(collectionView);
+                       Grid.SetRow(collectionView, 2);
+
+                       Content = layout;
+
+                       generator.GenerateItems();
+               }
+       }
+}
\ No newline at end of file
index b5fea2e..2b60a31 100644 (file)
                        generator.GenerateItems();
                }
        }
-
-       internal class ObservableCollectionResetGallery : ContentPage
-       {
-               public ObservableCollectionResetGallery()
-               {
-                       var layout = new Grid
-                       {
-                               RowDefinitions = new RowDefinitionCollection
-                               {
-                                       new RowDefinition { Height = GridLength.Auto },
-                                       new RowDefinition { Height = GridLength.Auto },
-                                       new RowDefinition { Height = GridLength.Star }
-                               }
-                       };
-
-                       var itemsLayout = new GridItemsLayout(3, ItemsLayoutOrientation.Vertical) as IItemsLayout;
-
-                       var itemTemplate = ExampleTemplates.PhotoTemplate();
-
-                       var collectionView = new CollectionView { ItemsLayout = itemsLayout, ItemTemplate = itemTemplate };
-
-                       var generator = new ItemsSourceGenerator(collectionView, 100, ItemsSourceType.MultiTestObservableCollection);
-
-                       layout.Children.Add(generator);
-
-                       var resetter = new Resetter(collectionView);
-                       layout.Children.Add(resetter);
-                       Grid.SetRow(resetter, 1);
-
-                       layout.Children.Add(collectionView);
-                       Grid.SetRow(collectionView, 2);
-
-                       Content = layout;
-
-                       generator.GenerateItems();
-               }
-       }
 }
\ No newline at end of file
index 3b7ecb8..4661584 100644 (file)
@@ -19,6 +19,10 @@ namespace Xamarin.Forms.Platform.iOS
                        ((INotifyCollectionChanged)itemSource).CollectionChanged += CollectionChanged;
                }
 
+               public int Count => _itemsSource.Count;
+
+               public object this[int index] => _itemsSource[index];
+
                void CollectionChanged(object sender, NotifyCollectionChangedEventArgs args)
                {
                        switch (args.Action)
@@ -44,18 +48,39 @@ namespace Xamarin.Forms.Platform.iOS
 
                void Move(NotifyCollectionChangedEventArgs args)
                {
-                       var oldPath = NSIndexPath.Create(0, args.OldStartingIndex);
-                       var newPath = NSIndexPath.Create(0, args.NewStartingIndex);
+                       var count = args.NewItems.Count;
+
+                       if (count == 1)
+                       {
+                               // For a single item, we can use MoveItem and get the animation
+                               var oldPath = NSIndexPath.Create(0, args.OldStartingIndex);
+                               var newPath = NSIndexPath.Create(0, args.NewStartingIndex);
+
+                               _collectionView.MoveItem(oldPath, newPath);
+                               return;
+                       }
 
-                       _collectionView.MoveItem(oldPath, newPath);
+                       var start = Math.Min(args.OldStartingIndex, args.NewStartingIndex);
+                       var end = Math.Max(args.OldStartingIndex, args.NewStartingIndex) + count;
+                       _collectionView.ReloadItems(CreateIndexesFrom(start, end));
                }
                
                private void Replace(NotifyCollectionChangedEventArgs args)
                {
-                       var startIndex = args.NewStartingIndex > -1 ? args.NewStartingIndex : _itemsSource.IndexOf(args.NewItems[0]);
-                       var count = args.NewItems.Count;
+                       var newCount = args.NewItems.Count;
 
-                       _collectionView.ReloadItems(CreateIndexesFrom(startIndex, count));
+                       if (newCount == args.OldItems.Count)
+                       {
+                               var startIndex = args.NewStartingIndex > -1 ? args.NewStartingIndex : _itemsSource.IndexOf(args.NewItems[0]);
+
+                               // We are replacing one set of items with a set of equal size; we can do a simple item range update
+                               _collectionView.ReloadItems(CreateIndexesFrom(startIndex, newCount));
+                               return;
+                       }
+                       
+                       // The original and replacement sets are of unequal size; this means that everything currently in view will 
+                       // have to be updated. So we just have to use ReloadData and let the UICollectionView update everything
+                       _collectionView.ReloadData();
                }
 
                static NSIndexPath[] CreateIndexesFrom(int startIndex, int count)
@@ -80,14 +105,19 @@ namespace Xamarin.Forms.Platform.iOS
 
                void Remove(NotifyCollectionChangedEventArgs args)
                {
-                       var startIndex = args.OldStartingIndex > -1 ? args.OldStartingIndex : _itemsSource.IndexOf(args.OldItems[0]);
-                       var count = args.OldItems.Count;
+                       var startIndex = args.OldStartingIndex;
+
+                       if (startIndex < 0)
+                       {
+                               // INCC implementation isn't giving us enough information to know where the removed items were in the
+                               // collection. So the best we can do is a ReloadData()
+                               _collectionView.ReloadData();
+                               return;
+                       }
 
+                       // If we have a start index, we can be more clever about removing the item(s) (and get the nifty animations)
+                       var count = args.OldItems.Count;
                        _collectionView.DeleteItems(CreateIndexesFrom(startIndex, count));
                }
-
-               public int Count => _itemsSource.Count;
-
-               public object this[int index] => _itemsSource[index];
        }
 }
\ No newline at end of file