diff --git a/src/Controls/src/Core/Handlers/Items/iOS/GroupableItemsViewController.cs b/src/Controls/src/Core/Handlers/Items/iOS/GroupableItemsViewController.cs
index 3e618c05deb3..f1064d9abe34 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/GroupableItemsViewController.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/GroupableItemsViewController.cs
@@ -173,17 +173,9 @@ internal CGSize GetReferenceSizeForheaderOrFooter(UICollectionView collectionVie
return CGSize.Empty;
}
- if (!(OperatingSystem.IsIOSVersionAtLeast(11) || OperatingSystem.IsTvOSVersionAtLeast(11)))
- {
- // iOS 10 crashes if we try to dequeue a cell for measurement
- // so we'll use an alternate method
- return MeasureSupplementaryView(elementKind, section);
- }
-
- var cell = GetViewForSupplementaryElement(collectionView, elementKind,
- NSIndexPath.FromItemSection(0, section)) as ItemsViewCell;
-
- return cell.Measure();
+ // Dequeuing a supplementary view for measurement caused multiple instances of the header/footer to appear in the view.
+ // We now always use MeasureSupplementaryView, an alternate approach for calculating the size without dequeuing the view.
+ return MeasureSupplementaryView(elementKind, section);
}
internal void SetScrollAnimationEndedCallback(Action callback)
diff --git a/src/Controls/src/Core/Handlers/Items/iOS/ObservableGroupedSource.cs b/src/Controls/src/Core/Handlers/Items/iOS/ObservableGroupedSource.cs
index feba35a2451f..050133eb7569 100644
--- a/src/Controls/src/Core/Handlers/Items/iOS/ObservableGroupedSource.cs
+++ b/src/Controls/src/Core/Handlers/Items/iOS/ObservableGroupedSource.cs
@@ -204,7 +204,7 @@ bool NotLoadedYet()
void Add(NotifyCollectionChangedEventArgs args)
{
- if (ReloadRequired())
+ if (NotLoadedYet())
{
Reload();
return;
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewAddGroupWhenViewIsEmpty.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewAddGroupWhenViewIsEmpty.png
new file mode 100644
index 000000000000..5d661ee0c610
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewAddGroupWhenViewIsEmpty.png differ
diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewDuplicateViewsWhenAddItemToGroup.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewDuplicateViewsWhenAddItemToGroup.png
new file mode 100644
index 000000000000..cb1e0f385aa5
Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/CollectionViewDuplicateViewsWhenAddItemToGroup.png differ
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue17969.xaml b/src/Controls/tests/TestCases.HostApp/Issues/Issue17969.xaml
new file mode 100644
index 000000000000..aab6b5922577
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue17969.xaml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue17969.xaml.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue17969.xaml.cs
new file mode 100644
index 000000000000..3fad15402b5e
--- /dev/null
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue17969.xaml.cs
@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using Microsoft.Maui.Controls;
+
+namespace Maui.Controls.Sample.Issues
+{
+
+ [Issue(IssueTracker.Github, 17969, "CollectionView duplicates group headers/footers when adding a new item to a group or crashes when adding a new group with empty view", PlatformAffected.iOS)]
+ public partial class Issue17969 : ContentPage
+ {
+ GroupedCollectionViewModel viewModel;
+ public Issue17969()
+ {
+ InitializeComponent();
+ viewModel = new GroupedCollectionViewModel();
+ BindingContext = viewModel;
+ }
+
+ private void OnAddClicked(object sender, EventArgs e)
+ {
+ viewModel.AddAnimal();
+ }
+
+ private void OnResetClicked(object sender, EventArgs e)
+ {
+ viewModel.Reset();
+ }
+
+ }
+
+ public class TestItem
+ {
+ public string Name { get; set; }
+ }
+ public class Animal
+ {
+ public string Name { get; set; }
+ }
+
+ public class AnimalGroup : ObservableCollection
+ {
+ public string Name { get; private set; }
+
+ public AnimalGroup(string name, List animals) : base(animals)
+ {
+ Name = name;
+ }
+ }
+
+ public class GroupedCollectionViewModel
+ {
+ public GroupedCollectionViewModel()
+ {
+ Init();
+
+ }
+
+ private void Init()
+ {
+ AnimalBaseList.Clear();
+ AnimalBaseList.Add(new AnimalGroup("Bears", new List
+ {
+ new Animal
+ {
+ Name = "American Black Bear",
+ },
+ new Animal
+ {
+ Name = "Asian Black Bear",
+ },
+ new Animal
+ {
+ Name = "Brown Bear",
+ },
+ new Animal
+ {
+ Name = "Grizzly-Polar Bear Hybrid",
+ },
+ new Animal
+ {
+ Name = "Sloth Bear",
+ },
+ new Animal
+ {
+ Name = "Sun Bear",
+ },
+ new Animal
+ {
+ Name = "Polar Bear",
+ },
+ new Animal
+ {
+ Name = "Spectacled Bear",
+ },
+ new Animal
+ {
+ Name = "Short-faced Bear",
+ },
+ new Animal
+ {
+ Name = "California Grizzly Bear",
+ }
+ }));
+
+ AnimalBaseList.Add(new AnimalGroup("Cats", new List
+ {
+ new Animal
+ {
+ Name = "Abyssinian",
+ },
+ new Animal
+ {
+ Name = "Arabian Mau",
+ },
+ new Animal
+ {
+ Name = "Bengal",
+ },
+ new Animal
+ {
+ Name = "Burmese",
+ },
+ new Animal
+ {
+ Name = "Cyprus",
+ },
+ new Animal
+ {
+ Name = "German Rex",
+ },
+ new Animal
+ {
+ Name = "Highlander",
+ },
+ new Animal
+ {
+ Name = "Peterbald",
+ },
+ new Animal
+ {
+ Name = "Scottish Fold",
+ },
+ new Animal
+ {
+ Name = "Sphynx",
+ }
+ }));
+
+ Animals.Add(new AnimalGroup(AnimalBaseList[0].Name, new List()));
+
+ AddAnimal();
+
+ Animals.Add(new AnimalGroup(AnimalBaseList[1].Name, AnimalBaseList[1].ToList()));
+
+ }
+
+
+ public bool AddAnimal()
+ {
+ // Add animal from first group
+ var sourceGroup = AnimalBaseList.First();
+ var targetGroup = Animals.First();
+
+ if (sourceGroup.Count == 0)
+ return false;
+
+ var animal = sourceGroup.First();
+ targetGroup.Add(animal);
+ sourceGroup.Remove(animal);
+
+ return true;
+ }
+
+ public void Reset()
+ {
+ Animals.Clear();
+ Init();
+
+ }
+
+ private List AnimalBaseList { get; set; } = new List();
+
+ public ObservableCollection Animals { get; private set; } = new ObservableCollection();
+ }
+
+}
\ No newline at end of file
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17969.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17969.cs
new file mode 100644
index 000000000000..73f4399277bd
--- /dev/null
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue17969.cs
@@ -0,0 +1,39 @@
+#if !MACCATALYST
+using NUnit.Framework;
+using UITest.Appium;
+using UITest.Core;
+
+namespace Microsoft.Maui.TestCases.Tests.Issues
+{
+ public class Issue17969 : _IssuesUITest
+ {
+
+ public Issue17969(TestDevice device)
+ : base(device)
+ { }
+
+ public override string Issue => "CollectionView duplicates group headers/footers when adding a new item to a group or crashes when adding a new group with empty view";
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ [FailsOnWindows]
+ public void CollectionViewDuplicateViewsWhenAddItemToGroup()
+ {
+ App.WaitForElement("collectionView");
+ App.Tap("addItem");
+ VerifyScreenshot();
+
+ }
+
+ [Test]
+ [Category(UITestCategories.CollectionView)]
+ [FailsOnWindows]
+ public void CollectionViewAddGroupWhenViewIsEmpty()
+ {
+ App.WaitForElement("collectionView");
+ App.Tap("addGroup");
+ VerifyScreenshot();
+ }
+ }
+}
+#endif
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewAddGroupWhenViewIsEmpty.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewAddGroupWhenViewIsEmpty.png
new file mode 100644
index 000000000000..90ce2112d0a6
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewAddGroupWhenViewIsEmpty.png differ
diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewDuplicateViewsWhenAddItemToGroup.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewDuplicateViewsWhenAddItemToGroup.png
new file mode 100644
index 000000000000..8a2f1fd28230
Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/CollectionViewDuplicateViewsWhenAddItemToGroup.png differ