[iOS] If the UITableView hasn't rendered anything yet don't try to sync cells (#3090) fixes #1342 fixes #1542 fixes #1927

* [iOS] fixes #1342 - verify the UITableView has rendered cells before trying to synchronize to collection

* - let reset through on CollectionChanged
This commit is contained in:
Shane Neuville 2018-06-22 09:08:56 -06:00 коммит произвёл Rui Marinho
Родитель 781165fabd
Коммит d6810346d1
3 изменённых файлов: 176 добавлений и 0 удалений

Просмотреть файл

@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
using System.Windows.Input;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
#if UITEST
using Xamarin.UITest;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif
namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 1342, "[iOS] ListView throws Exception on ObservableCollection.Add/Remove for non visible list view",
PlatformAffected.iOS)]
#if UITEST
[NUnit.Framework.Category(UITestCategories.ListView)]
#endif
public class Issue1342 : TestNavigationPage
{
const string add2 = "add2";
const string add3 = "add3";
const string success = "No crash means success";
protected override void Init()
{
PushAsync(new MainPageCode
{
BindingContext = new MainViewModel
{
ViewModel1 = new ListViewModel
{
Items = new ObservableCollection<string>(new[] { $"Click {add2}", $"Click {add3}", success })
},
ViewModel2 = new ListViewModel
{
Items = new ObservableCollection<string>(new[] { "item2.1", "item2.2", "item2.3" })
},
ViewModel3 = new ListViewModel
{
Items = new ObservableCollection<string>()
}
}
});
}
[Preserve(AllMembers = true)]
public partial class MainPageCode : TabbedPage
{
public MainPageCode()
{
ToolbarItems.Add(new Xamarin.Forms.ToolbarItem() { Text = "add1" });
ToolbarItems.Add(new Xamarin.Forms.ToolbarItem() { Text = $"{add2}" });
ToolbarItems.Add(new Xamarin.Forms.ToolbarItem() { Text = $"{add3}" });
ToolbarItems.Add(new Xamarin.Forms.ToolbarItem() { Text = "reload" });
ToolbarItems.Add(new Xamarin.Forms.ToolbarItem() { Text = "visible" });
ToolbarItems[0].SetBinding(ToolbarItem.CommandProperty, "Add1Command");
ToolbarItems[1].SetBinding(ToolbarItem.CommandProperty, "Add2Command");
ToolbarItems[2].SetBinding(ToolbarItem.CommandProperty, "Add3Command");
ToolbarItems[3].SetBinding(ToolbarItem.CommandProperty, "Add4Command");
ToolbarItems[4].SetBinding(ToolbarItem.CommandProperty, "Add5Command");
ListPageCode page = new ListPageCode();
page.SetBinding(ListPageCode.BindingContextProperty, "ViewModel1");
Children.Add(page);
page = new ListPageCode();
page.SetBinding(ListPageCode.BindingContextProperty, "ViewModel2");
Children.Add(page);
page = new ListPageCode();
page.SetBinding(ListPageCode.BindingContextProperty, "ViewModel3");
Children.Add(page);
}
}
[Preserve(AllMembers = true)]
public class MainViewModel
{
void AddItems(ObservableCollection<string> list)
{
list.Add("new item");
}
public MainViewModel()
{
Add1Command = new Command(() => AddItems(ViewModel1.Items));
Add2Command = new Command(() => AddItems(ViewModel2.Items));
Add3Command = new Command(() => AddItems(ViewModel3.Items));
Add4Command = new Command(() =>
{
ViewModel1.ReloadData();
ViewModel2.ReloadData();
ViewModel3.ReloadData();
});
Add5Command = new Command(() =>
{
ViewModel1.ChangeListViewVisability();
ViewModel2.ChangeListViewVisability();
ViewModel3.ReloadData();
});
}
public ListViewModel ViewModel1 { get; set; }
public ListViewModel ViewModel2 { get; set; }
public ListViewModel ViewModel3 { get; set; }
public ICommand Add1Command { get; }
public ICommand Add2Command { get; }
public ICommand Add3Command { get; }
public ICommand Add4Command { get; }
public ICommand Add5Command { get; }
}
[Preserve(AllMembers = true)]
public class ListViewModel : INotifyPropertyChanged
{
public ObservableCollection<string> Items { get; set; }
public bool IsVisible { get; set; } = true;
public event PropertyChangedEventHandler PropertyChanged;
public void ReloadData()
{
Items = new ObservableCollection<string>();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Items)));
}
public void ChangeListViewVisability()
{
IsVisible = !IsVisible;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsVisible)));
}
}
[Preserve(AllMembers = true)]
public partial class ListPageCode : ContentPage
{
public ListPageCode()
{
Icon = "coffee.png";
ListView view = new ListView(ListViewCachingStrategy.RecycleElement);
Content = view;
view.SetBinding(ListView.ItemsSourceProperty, "Items");
view.SetBinding(ListView.IsVisibleProperty, "IsVisible");
}
}
#if UITEST
[Test]
public void AddingItemsToNonVisibleListViewDoesntCrash()
{
RunningApp.Tap(add2);
RunningApp.Tap(add3);
RunningApp.WaitForElement(success);
}
#endif
}
}

Просмотреть файл

@ -244,6 +244,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue1799.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue1931.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue3089.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue1342.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue2767.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue2499.cs" />
<Compile Include="$(MSBuildThisFileDirectory)GitHub1878.cs" />

Просмотреть файл

@ -530,6 +530,11 @@ namespace Xamarin.Forms.Platform.iOS
if (exArgs != null)
_dataSource.Counts[section] = exArgs.Count;
// This means the UITableView hasn't rendered any cells yet
// so there's no need to synchronize the rows on the UITableView
if (Control.IndexPathsForVisibleRows == null && e.Action != NotifyCollectionChangedAction.Reset)
return;
var groupReset = resetWhenGrouped && Element.IsGroupingEnabled;
// We can't do this check on grouped lists because the index doesn't match the number of rows in a section.