Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

[iOS] Fix issue disposing the EmptyView #12377

Merged
merged 5 commits into from
Nov 16, 2020
Merged
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
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="utf-8" ?>
<controls:TestContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="Xamarin.Forms.Controls.Issues.Issue12374"
Title="Issue 12374">
<Grid RowDefinitions="Auto,Auto,*">
<Label
Grid.Row="0"
AutomationId = "TestReady"
Padding="12"
BackgroundColor="Black"
TextColor="White"
Text="Tap each item to remove them and view the EmptyView. Then, tap the AddItems button. Without crash, the test has passed."/>
<StackLayout
Grid.Row="1"
Orientation="Horizontal"
HorizontalOptions="Center"
Margin="20">
<Button
AutomationId="AddItems"
Text="Add Items"
Clicked="AddItemsButton_Clicked" />
<Button
AutomationId="RemoveItems"
Text="Remove Items"
Clicked="RemoveItemsButton_Clicked" />
</StackLayout>
<RefreshView
Grid.Row="2"
x:Name="MainRefreshView"
IsRefreshing="{Binding IsRefreshing}"
Command="{Binding RefreshCommand}">
<CollectionView
Grid.Row="1"
ItemsSource="{Binding Items}"
SelectionMode="Single"
SelectionChanged="CollectionView_SelectionChanged">
<CollectionView.EmptyView>
<Grid>
<Label
Text="Empty View"
HorizontalOptions="Center"
VerticalOptions="Center" />
</Grid>
</CollectionView.EmptyView>
<!--
<CollectionView.EmptyViewTemplate>
<DataTemplate>
<Grid>
<Label Text="Empty Template View" HorizontalOptions="Center" VerticalOptions="Center" />
</Grid>
</DataTemplate>
</CollectionView.EmptyViewTemplate>
-->
<CollectionView.ItemTemplate>
<DataTemplate>
<!--
<SwipeView>
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem Text="Delete" BackgroundColor="Red" Invoked="Item_SwipeInvoked" />
</SwipeItems>
</SwipeView.RightItems>
<Grid HeightRequest="40" Padding="10" BackgroundColor="{Binding Color}">
<Label Text="{Binding Name}" VerticalOptions="Center" />
</Grid>
</SwipeView>
-->
<Grid
HeightRequest="40"
Padding="10"
BackgroundColor="{Binding Color}">
<Label
Text="{Binding Name}"
VerticalOptions="Center" />
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
</Grid>
</controls:TestContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
using System;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using System.Windows.Input;
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.CollectionView)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 12374, "[Bug] [iOS] CollectionView EmptyView causes the application to crash",
PlatformAffected.iOS)]
public partial class Issue12374 : TestContentPage
{
int _itemNumber = 0;
bool _isRefreshing;

public Issue12374()
{
#if APP
InitializeComponent();

AddItems();

BindingContext = this;
#endif
}

protected override void Init()
{

}

public bool IsRefreshing
{
get { return _isRefreshing; }
set
{
_isRefreshing = value;
OnPropertyChanged();
}
}

public ObservableCollection<Issue12374Model> Items { get; private set; } = new ObservableCollection<Issue12374Model>();

public ICommand RefreshCommand => new Command(async () => await RefreshItemsAsync());

async Task RefreshItemsAsync()
{
IsRefreshing = true;
await Task.Delay(TimeSpan.FromSeconds(1));
AddItems();
IsRefreshing = false;
}

void AddItems()
{
var random = new Random();
for (int i = 0; i < 2; i++)
{
Items.Add(new Issue12374Model()
{
Guid = Guid.NewGuid(),
Color = Color.FromRgb(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)),
Name = $"Item {_itemNumber++}"
});
}
}

#if APP
public void Item_SwipeInvoked(object sender, EventArgs e)
{
if (sender is SwipeItem swipeItem && swipeItem.BindingContext is Issue12374Model item)
{
Items.Remove(item);
}
}

void AddItemsButton_Clicked(object sender, EventArgs e)
{
AddItems();
}

void RemoveItemsButton_Clicked(object sender, EventArgs e)
{
Items.Clear();
}

void CollectionView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.CurrentSelection.Count == 0)
return;

if (sender is CollectionView collectionView)
{
collectionView.SelectedItems = null;
}

if (e.CurrentSelection[0] is Issue12374Model item)
{
Items.Remove(item);
}
}
#endif

#if UITEST
[Test]
public void Issue12374Test()
{
RunningApp.WaitForElement("TestReady");
RunningApp.Tap("RemoveItems");
RunningApp.Tap("AddItems");
RunningApp.Tap("RemoveItems");
RunningApp.Screenshot("CollectionViewWithEmptyView");
}
#endif
}

[Preserve(AllMembers = true)]
public class Issue12374Model : IEquatable<Issue12374Model>
{
public Guid Guid { get; set; }
public string Name { get; set; }
public Color Color { get; set; }

public bool Equals(Issue12374Model other)
{
return other.Guid.Equals(Guid);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue11081.xaml.cs">
<DependentUpon>Issue11081.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue12374.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue12222.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue11911.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue11691.xaml.cs" />
Expand Down Expand Up @@ -1954,6 +1955,9 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue11081.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue12374.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue11911.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
Expand Down
16 changes: 12 additions & 4 deletions Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,15 +289,20 @@ internal void UpdateEmptyView()
protected virtual CGRect DetermineEmptyViewFrame()
{
return new CGRect(CollectionView.Frame.X, CollectionView.Frame.Y,
CollectionView.Frame.Width, CollectionView.Frame.Height);
CollectionView.Frame.Width, CollectionView.Frame.Height);
}

void LayoutEmptyView()
{
var frame = DetermineEmptyViewFrame();
if (_emptyUIView == null)
{
UpdateEmptyView();
return;
}

var frame = DetermineEmptyViewFrame();

if (_emptyUIView != null)
_emptyUIView.Frame = frame;
_emptyUIView.Frame = frame;

if (_emptyViewFormsElement != null && ItemsView.LogicalChildren.Contains(_emptyViewFormsElement))
_emptyViewFormsElement.Layout(frame.ToRectangle());
Expand Down Expand Up @@ -387,6 +392,9 @@ void UpdateEmptyViewVisibility(bool isEmpty)
if (_emptyViewDisplayed)
{
_emptyUIView.RemoveFromSuperview();
_emptyUIView.Dispose();
_emptyUIView = null;

ItemsView.RemoveLogicalChild(_emptyViewFormsElement);
}

Expand Down