diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue4314.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue4314.cs new file mode 100644 index 00000000000..b57526eb2ab --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue4314.cs @@ -0,0 +1,57 @@ +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + +#if UITEST +using Xamarin.Forms.Core.UITests; +using Xamarin.UITest; +using NUnit.Framework; +#endif + +namespace Xamarin.Forms.Controls.Issues +{ +#if UITEST + [Category(UITestCategories.ContextActions)] +#endif + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 4314, "When ListView items is removed and it is empty, Xamarin Forms crash", PlatformAffected.iOS)] + public class Issue4314 : TestNavigationPage // or TestMasterDetailPage, etc ... + { + const string Success = "Success"; +#if !UITEST + MessagesViewModel viewModel; + protected override void Init() + { + var page = new ContextActionsGallery(false, true, 2) { Title = "Swipe and delete both" }; + viewModel = page.BindingContext as MessagesViewModel; + viewModel.Messages.CollectionChanged += (s, e) => + { + if (viewModel.Messages.Count == 0) + { + Navigation.PushAsync(new ContentPage { Title = "Success", Content = new Label { Text = Success } }); + } + }; + Navigation.PushAsync(page); + } +#else + protected override void Init() + { + } +#endif +#if UITEST && __IOS__ + [Test] + public void Issue4341Test() + { + RunningApp.WaitForElement(c=> c.Marked("Email")); + RunningApp.ActivateContextMenu("Subject Line 0"); + RunningApp.WaitForElement("Delete"); + RunningApp.Tap("Delete"); + RunningApp.ActivateContextMenu("Subject Line 1"); + RunningApp.Tap("Delete"); + RunningApp.WaitForElement(c=> c.Marked(Success)); + RunningApp.Back(); + RunningApp.WaitForElement(c => c.Marked("Email")); + RunningApp.SwipeRightToLeft(); + } +#endif + } +} \ No newline at end of file diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index b53cbe8dd2c..f65145b0e51 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -820,6 +820,7 @@ + diff --git a/Xamarin.Forms.Controls/GalleryPages/EditableList.cs b/Xamarin.Forms.Controls/GalleryPages/EditableList.cs index e2265726bf7..3f98473deaf 100644 --- a/Xamarin.Forms.Controls/GalleryPages/EditableList.cs +++ b/Xamarin.Forms.Controls/GalleryPages/EditableList.cs @@ -7,9 +7,9 @@ namespace Xamarin.Forms.Controls { internal class MessagesViewModel : ViewModelBase { - public MessagesViewModel() + public MessagesViewModel(int messagesCount) { - Messages = new ObservableCollection (Enumerable.Range (0, 100).Select (i => { + Messages = new ObservableCollection (Enumerable.Range (0, messagesCount).Select (i => { return new MessageViewModel { Subject = "Subject Line " + i, MessagePreview = "Lorem ipsum dolorem monkeys bonkers " + i }; })); @@ -26,7 +26,7 @@ public ObservableCollection Messages } [Preserve (AllMembers = true)] - internal class MessageViewModel : ViewModelBase + public class MessageViewModel : ViewModelBase { public MessageViewModel() { @@ -86,13 +86,16 @@ public MessageCell() } } - public ContextActionsGallery (bool tableView = false) + public ContextActionsGallery (bool tableView = false, bool hasUnevenRows = false, int messagesCount = 100) { - BindingContext = new MessagesViewModel(); + BindingContext = new MessagesViewModel(messagesCount); View list; if (!tableView) { - list = new ListView(); + list = new ListView + { + HasUnevenRows = hasUnevenRows + }; list.SetBinding (ListView.ItemsSourceProperty, "Messages"); ((ListView)list).ItemTemplate = new DataTemplate (typeof (MessageCell)); } else { diff --git a/Xamarin.Forms.Platform.iOS/ContextActionCell.cs b/Xamarin.Forms.Platform.iOS/ContextActionCell.cs index 480651a5e2b..6760cc531fc 100644 --- a/Xamarin.Forms.Platform.iOS/ContextActionCell.cs +++ b/Xamarin.Forms.Platform.iOS/ContextActionCell.cs @@ -276,6 +276,7 @@ protected override void Dispose(bool disposing) if (_scroller != null) { + _scroller.Delegate = null; _scroller.Dispose(); _scroller = null; } diff --git a/Xamarin.Forms.Platform.iOS/ContextScrollViewDelegate.cs b/Xamarin.Forms.Platform.iOS/ContextScrollViewDelegate.cs index 22f57fc3e85..82a674772bd 100644 --- a/Xamarin.Forms.Platform.iOS/ContextScrollViewDelegate.cs +++ b/Xamarin.Forms.Platform.iOS/ContextScrollViewDelegate.cs @@ -125,7 +125,7 @@ public override void Scrolled(UIScrollView scrollView) RestoreHighlight(scrollView); s_scrollViewBeingScrolled = null; - ClearCloserRecognizer(scrollView); + ClearCloserRecognizer(GetContextCell(scrollView)); ClosedCallback?.Invoke(); } } @@ -133,7 +133,7 @@ public override void Scrolled(UIScrollView scrollView) public void Unhook(UIScrollView scrollView) { RestoreHighlight(scrollView); - ClearCloserRecognizer(scrollView); + ClearCloserRecognizer(GetContextCell(scrollView)); } public override void WillEndDragging(UIScrollView scrollView, PointF velocity, ref PointF targetContentOffset) @@ -158,36 +158,35 @@ public override void WillEndDragging(UIScrollView scrollView, PointF velocity, r while (view.Superview != null) { view = view.Superview; - - NSAction close = () => - { - if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) - RestoreHighlight(scrollView); - - IsOpen = false; - scrollView.SetContentOffset(new PointF(0, 0), true); - - ClearCloserRecognizer(scrollView); - }; - var table = view as UITableView; if (table != null) { + ContextActionsCell contentCell = GetContextCell(scrollView); + NSAction close = () => + { + if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0)) + RestoreHighlight(scrollView); + + IsOpen = false; + scrollView.SetContentOffset(new PointF(0, 0), true); + ClearCloserRecognizer(contentCell); + contentCell = null; + }; + _table = table; _globalCloser = new GlobalCloseContextGestureRecognizer(scrollView, close); _globalCloser.ShouldRecognizeSimultaneously = (recognizer, r) => r == _table.PanGestureRecognizer; table.AddGestureRecognizer(_globalCloser); _closer = new UITapGestureRecognizer(close); - var cell = GetContextCell(scrollView); - cell.ContentCell.AddGestureRecognizer(_closer); + contentCell.AddGestureRecognizer(_closer); } } } } else { - ClearCloserRecognizer(scrollView); + ClearCloserRecognizer(GetContextCell(scrollView)); IsOpen = false; targetContentOffset = new PointF(0, 0); @@ -234,13 +233,12 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - void ClearCloserRecognizer(UIScrollView scrollView) + void ClearCloserRecognizer(ContextActionsCell cell) { if (_globalCloser == null || _globalCloser.State == UIGestureRecognizerState.Cancelled) return; - var cell = GetContextCell(scrollView); - cell.ContentCell.RemoveGestureRecognizer(_closer); + cell?.ContentCell?.RemoveGestureRecognizer(_closer); _closer.Dispose(); _closer = null; @@ -252,9 +250,9 @@ void ClearCloserRecognizer(UIScrollView scrollView) ContextActionsCell GetContextCell(UIScrollView scrollView) { - var view = scrollView.Superview.Superview; + var view = scrollView?.Superview?.Superview; var cell = view as ContextActionsCell; - while (view.Superview != null) + while (view?.Superview != null) { cell = view as ContextActionsCell; if (cell != null)