diff --git a/src/Controls/src/Core/SwipeView/SwipeView.cs b/src/Controls/src/Core/SwipeView/SwipeView.cs index 4b7351f43845..c419f0b85509 100644 --- a/src/Controls/src/Core/SwipeView/SwipeView.cs +++ b/src/Controls/src/Core/SwipeView/SwipeView.cs @@ -128,6 +128,18 @@ protected override void OnBindingContextChanged() SetInheritedBindingContext(RightItems, BindingContext); SetInheritedBindingContext(TopItems, BindingContext); SetInheritedBindingContext(BottomItems, BindingContext); + + // In Windows and Android, new cells are created when rebinding, so _isOpen is false. + // In iOS, CollectionView reuses cells, so SwipeView remain open (_isOpen = true). + // When new cells are recreated, the swipe state is reset. + // When cells are reused (iOS), the open state is maintained. + // Fix for iOS: Close SwipeView when BindingContext changes to prevent stale open state +#if IOS || MACCATALYST + if (_isOpen) + { + ((ISwipeView)this).RequestClose(new SwipeViewCloseRequest(false)); + } +#endif } static void OnSwipeItemsChanged(BindableObject bindable, object oldValue, object newValue) diff --git a/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SwipeItemShouldBeCloseWhenUpdateTheCollectionView.png b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SwipeItemShouldBeCloseWhenUpdateTheCollectionView.png new file mode 100644 index 000000000000..b7beb21d5640 Binary files /dev/null and b/src/Controls/tests/TestCases.Android.Tests/snapshots/android/SwipeItemShouldBeCloseWhenUpdateTheCollectionView.png differ diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue19541.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue19541.cs new file mode 100644 index 000000000000..370e66d5f7e7 --- /dev/null +++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue19541.cs @@ -0,0 +1,127 @@ +using System.Collections.ObjectModel; + +namespace Maui.Controls.Sample.Issues; + +[Issue(IssueTracker.Github, 19541, "[iOS] - Swipeview with collectionview issue", PlatformAffected.iOS)] + +public class Issue19541 : ContentPage +{ + SwipeView _swipeView; + CollectionView collectionView; + + public Issue19541() + { + // Root layout + var rootLayout = new VerticalStackLayout(); + + // Refresh button + var refreshButton = new Button + { + Text = "Refresh List", + AutomationId = "RefreshButton", + Margin = new Thickness(10) + }; + refreshButton.Clicked += OnRefreshClicked; + + // Open button + var openButton = new Button + { + Text = "Open", + AutomationId = "OpenButton" + }; + openButton.Clicked += Button_Clicked; + + // CollectionView + collectionView = new CollectionView(); + + // Define ItemTemplate in code + collectionView.ItemTemplate = new DataTemplate(() => + { + var swipeView = new SwipeView + { + HeightRequest = 60 + }; + + swipeView.Loaded += swipeView_Loaded; + + var label = new Label(); + label.SetBinding(Label.TextProperty, "Name"); + + // Swipe RightItems + var swipeItems = new SwipeItems + { + SwipeBehaviorOnInvoked = SwipeBehaviorOnInvoked.Close + }; + + var swipeItemView = new SwipeItemView + { + Content = new Button + { + BackgroundColor = Colors.Pink, + Text = "Delete" + } + }; + + swipeItems.Add(swipeItemView); + swipeView.RightItems = swipeItems; + + swipeView.Content = label; + + return swipeView; + }); + + // Add controls to layout + rootLayout.Children.Add(refreshButton); + rootLayout.Children.Add(openButton); + rootLayout.Children.Add(collectionView); + + Content = rootLayout; + + LoadInitialList(); + } + + void LoadInitialList() + { + var list = new List(); + for (int i = 0; i < 3; i++) + { + list.Add(new Issue19541Model + { + Name = $"Name {i}", + }); + } + + collectionView.ItemsSource = list; + } + + void OnRefreshClicked(object sender, EventArgs e) + { + var list = new List(); + for (int i = 0; i < 3; i++) + { + list.Add(new Issue19541Model + { + Name = $"Name {i}", + }); + } + + collectionView.ItemsSource = list; + } + + void swipeView_Loaded(object sender, EventArgs e) + { + if (_swipeView is null) + _swipeView = (SwipeView)sender; + } + + void Button_Clicked(object sender, EventArgs e) + { + _swipeView?.Open(OpenSwipeItem.RightItems); + } +} + +public class Issue19541Model +{ + public string Name { get; set; } +} + diff --git a/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwipeItemShouldBeCloseWhenUpdateTheCollectionView.png b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwipeItemShouldBeCloseWhenUpdateTheCollectionView.png new file mode 100644 index 000000000000..aca8e1aa5cbe Binary files /dev/null and b/src/Controls/tests/TestCases.Mac.Tests/snapshots/mac/SwipeItemShouldBeCloseWhenUpdateTheCollectionView.png differ diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19541.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19541.cs new file mode 100644 index 000000000000..d2b907dfba70 --- /dev/null +++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue19541.cs @@ -0,0 +1,26 @@ +#if TEST_FAILS_ON_WINDOWS //More info: https://github.com/dotnet/maui/issues/14777 +using NUnit.Framework; +using UITest.Appium; +using UITest.Core; + +namespace Microsoft.Maui.TestCases.Tests.Issues; + +public class Issue19541 : _IssuesUITest +{ + public Issue19541(TestDevice device) : base(device) + { + } + + public override string Issue => "[iOS] - Swipeview with collectionview issue"; + + [Test] + [Category(UITestCategories.SwipeView)] + public void SwipeItemShouldBeCloseWhenUpdateTheCollectionView() + { + App.WaitForElement("RefreshButton"); + App.Tap("OpenButton"); + App.Tap("RefreshButton"); + VerifyScreenshot(); + } +} +#endif \ No newline at end of file diff --git a/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SwipeItemShouldBeCloseWhenUpdateTheCollectionView.png b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SwipeItemShouldBeCloseWhenUpdateTheCollectionView.png new file mode 100644 index 000000000000..0c7ffe471144 Binary files /dev/null and b/src/Controls/tests/TestCases.iOS.Tests/snapshots/ios/SwipeItemShouldBeCloseWhenUpdateTheCollectionView.png differ