Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,22 @@ protected override UICollectionViewDelegateFlowLayout CreateDelegator()
// _Only_ called if the user initiates the selection change; will not be called for programmatic selection
public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath)
{
if (indexPath is null || indexPath.Handle == IntPtr.Zero)
Copy link

Copilot AI Jun 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar guard logic is repeated here—extract into a shared helper method to keep the code DRY and make future updates simpler.

Suggested change
if (indexPath is null || indexPath.Handle == IntPtr.Zero)
if (!IsValidIndexPath(indexPath))

Copilot uses AI. Check for mistakes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good suggestion IsValidIndexPath can be used for this check.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think IsValidIndexPath is only on ListViewRenderer?

One thing I'm wondering is if we should re-evalute the indexes when they are used inside of PerformBatchUpdates

like in this code

	CollectionView.PerformBatchUpdates(null, _ =>
				{
					CollectionView.SelectItem(index, true, UICollectionViewScrollPosition.None);
					CollectionView.CellForItem(index)?.UpdateSelectedAccessibility(true);
				});

if the index should be retrieved a second time and if it's changed at all then we don't process the update

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PureWeen The case you mentioned could occur if the ItemsSource is updated immediately after setting the SelectedItem property, as shown in the example below. In such scenarios, during the PerformBatchUpdates call, the CollectionView items may be modified, and the index might no longer exist in the collection. In this case, we do not proceed with updating the collection.

image

However, the originally reported issue is unrelated to that scenario. As seen in the stack trace, the issue originates from the ItemSelected override method, which is triggered when the user taps to select an item. This indicates that the crash is likely occurring during user interaction. On the other hand, the SelectItem method you referenced is only invoked during programmatic selection, which is not the case here.

image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sheiksyedm, will the changes in this PR also fix the issue caused by the 'ItemsSource updated immediately' problem?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bhavanesh2001 No, it will not fix the ItemsSource updated problem. This fix may only address the ObjectDisposedException observed in the reported stack trace.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://github.com/dotnet/maui/pull/29801/files#r2125125141

I'm wondering if we should re-evaluate the index when the performbatchupdate method is called and if that's the better overall fix here?

Or maybe we should change the ordering?

CollectionView.SelectItem(index, true, UICollectionViewScrollPosition.None);

Is going to trigger a behavior change reaction in the code
Would it make sense to have it like this instead

 CollectionView.CellForItem(index)?.UpdateSelectedAccessibility(true);
CollectionView.SelectItem(index, true, UICollectionViewScrollPosition.None);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think IsValidIndexPath is only on ListViewRenderer?

One thing I'm wondering is if we should re-evalute the indexes when they are used inside of PerformBatchUpdates

like in this code

	CollectionView.PerformBatchUpdates(null, _ =>
				{
					CollectionView.SelectItem(index, true, UICollectionViewScrollPosition.None);
					CollectionView.CellForItem(index)?.UpdateSelectedAccessibility(true);
				});

if the index should be retrieved a second time and if it's changed at all then we don't process the update

We have this one https://github.com/dotnet/maui/blob/main/src/Compatibility/Core/src/iOS/CollectionView/IndexPathHelpers.cs#L36

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well thats Compatibility , this is the one I think we use on the CV

public static bool IsIndexPathValid(this IItemsViewSource source, NSIndexPath indexPath)

{
return;
}

FormsSelectItem(indexPath);
}

// _Only_ called if the user initiates the selection change; will not be called for programmatic selection
public override void ItemDeselected(UICollectionView collectionView, NSIndexPath indexPath)
{
if (indexPath is null || indexPath.Handle == IntPtr.Zero)
{
return;
}

FormsDeselectItem(indexPath);
}

Expand All @@ -39,6 +49,11 @@ internal void SelectItem(object selectedItem)
{
Copy link
Contributor

@bhavanesh2001 bhavanesh2001 Jun 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To extend on @PureWeen’s comment here: #29801 (comment)
we can be more cautious when performing PerformBatchUpdates(...) by adding guards to ensure the selection logic applies to the current and valid ItemsSource.

Something like this might help avoid re-selecting stale or unintended items if the ItemsSource has changed between scheduling and executing the batch update:

internal void SelectItem(object selectedItem)
		{
			var originalSource = ItemsView.ItemsSource;
			var index = GetIndexForItem(selectedItem);

			if (index.Section > -1 && index.Item > -1)
			{
				// Ensure the selected index is updated after the collection view's items generation is completed
				CollectionView.PerformBatchUpdates(null, _ =>
				{
					// Exit if the ItemsSource reference no longer matches the one captured at invocation.
					if (!ReferenceEquals(ItemsView.ItemsSource, originalSource))
					{
						return;
					}

					// Recalculate the index for the selectedItem now that the collection may have changed.(Adding, deleting etc..)
					var updatedIndex = GetIndexForItem(selectedItem);
					if (updatedIndex.Section < 0 || updatedIndex.Item < 0)
					{
						return;
					}

					// Retrieve the current item at that index and verify it still equals the intended selection.
					var liveItem = GetItemAtIndex(updatedIndex);
					if (!Equals(liveItem, selectedItem))
					{
						return;
					}

					CollectionView.SelectItem(index, true, UICollectionViewScrollPosition.None);
					CollectionView.CellForItem(index)?.UpdateSelectedAccessibility(true);
				});
			}
		}

var index = GetIndexForItem(selectedItem);

if (index is null || index.Handle == IntPtr.Zero)
{
return;
}

if (index.Section > -1 && index.Item > -1)
{
// Ensure the selected index is updated after the collection view's items generation is completed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,22 @@ protected override UICollectionViewDelegateFlowLayout CreateDelegator()
// _Only_ called if the user initiates the selection change; will not be called for programmatic selection
public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath)
{
if (indexPath is null || indexPath.Handle == IntPtr.Zero)
Copy link

Copilot AI Jun 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same null-and-handle check is duplicated across methods—consider extracting this into a private helper (e.g., bool IsValidIndexPath(NSIndexPath path)) to reduce duplication and ease future maintenance.

Suggested change
if (indexPath is null || indexPath.Handle == IntPtr.Zero)
if (!IsValidIndexPath(indexPath))

Copilot uses AI. Check for mistakes.
{
return;
}

FormsSelectItem(indexPath);
}

// _Only_ called if the user initiates the selection change; will not be called for programmatic selection
public override void ItemDeselected(UICollectionView collectionView, NSIndexPath indexPath)
{
if (indexPath is null || indexPath.Handle == IntPtr.Zero)
{
return;
}

FormsDeselectItem(indexPath);
}

Expand All @@ -39,6 +49,11 @@ internal void SelectItem(object selectedItem)
{
var index = GetIndexForItem(selectedItem);

if (index is null || index.Handle == IntPtr.Zero)
{
return;
}

if (index.Section > -1 && index.Item > -1)
{
// Ensure the selected index is updated after the collection view's items generation is completed
Expand Down
Loading