diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla60563.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla60563.cs new file mode 100644 index 000000000..19ea1bb6a --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Bugzilla60563.cs @@ -0,0 +1,92 @@ +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; +using System.Collections.Generic; + +#if UITEST +using Xamarin.UITest; +using NUnit.Framework; +#endif + +namespace Xamarin.Forms.Controls.Issues +{ + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Bugzilla, 60563, "ActivityIndicator in ListView causes SIGSEGV crash in iOS 8", PlatformAffected.iOS)] + public class Bugzilla60563 : TestNavigationPage + { + const string btnGoToList = "btnGoToList"; + const string spinner = "spinner"; + + protected override void Init() + { + Navigation.PushAsync(new NavigationPage(new StartPage())); + } + + [Preserve(AllMembers = true)] + class ListPage : ContentPage + { + public ListPage() + { + Title = "List"; + Content = new ListView + { + HasUnevenRows = false, + RowHeight = 50, + ItemTemplate = new DataTemplate(() => { return new SpinnerViewCell(); }), + ItemsSource = new List { 1, 2, 3, 4, 5 }, + }; + } + } + + [Preserve(AllMembers = true)] + class SpinnerViewCell : ViewCell + { + public SpinnerViewCell() + { + var indicator = new ActivityIndicator + { + IsRunning = true, + AutomationId = spinner + }; + var layout = new RelativeLayout(); + layout.Children.Add(indicator, x: () => 0, y: () => 0); + View = indicator; + } + } + + [Preserve(AllMembers = true)] + class StartPage : ContentPage + { + public StartPage() + { + var button = new Button + { + Text = "Go To List", + BackgroundColor = Color.Beige, + HeightRequest = 40, + WidthRequest = 100, + VerticalOptions = LayoutOptions.Center, + HorizontalOptions = LayoutOptions.Center, + AutomationId = btnGoToList + }; + button.Clicked += (sender, e) => Navigation.PushAsync(new ListPage()); + + Title = "Home"; + Content = new StackLayout { Children = { new Label { Text = "Click the button to go to a ListView with an ActivityIndicator, then go back to this page. If the app does not crash, this test has passed." }, button } }; + } + } + +#if UITEST && __IOS__ + [Test] + public void Bugzilla60563Test() + { + RunningApp.WaitForElement(q => q.Marked(btnGoToList)); + RunningApp.Tap(q => q.Marked(btnGoToList)); + RunningApp.WaitForElement(q => q.Marked(spinner)); + RunningApp.Back(); + RunningApp.WaitForElement(q => q.Marked(btnGoToList)); + RunningApp.Tap(q => q.Marked(btnGoToList)); + RunningApp.WaitForElement(q => q.Marked(spinner)); + } +#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 aa0bffc86..0b2e8c768 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 @@ -226,6 +226,7 @@ + diff --git a/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs b/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs index 1c3a72514..0c1911cd2 100644 --- a/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs +++ b/Xamarin.Forms.Platform.iOS/Renderers/ListViewRenderer.cs @@ -102,10 +102,19 @@ namespace Xamarin.Forms.Platform.iOS void DisposeSubviews(UIView view) { - foreach (UIView subView in view.Subviews) - DisposeSubviews(subView); + var ver = view as IVisualElementRenderer; + + if (ver == null) + { + // VisualElementRenderers should implement their own dispose methods that will appropriately dispose and remove their child views. + // Attempting to do this work twice could cause a SIGSEGV (only observed in iOS8), so don't do this work here. + // Non-renderer views, such as separator lines, etc., can be removed here. + foreach (UIView subView in view.Subviews) + DisposeSubviews(subView); + + view.RemoveFromSuperview(); + } - view.RemoveFromSuperview(); view.Dispose(); }