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

Commit

Permalink
More intelligent cell resizing; fix infinite layout loop and crash;
Browse files Browse the repository at this point in the history
Reuse template views instead of re-creating them every time;
Fixes #7128
  • Loading branch information
hartez committed Aug 26, 2019
1 parent 24ad7e6 commit ed2b0de
Show file tree
Hide file tree
Showing 12 changed files with 393 additions and 135 deletions.
@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using System.ComponentModel;
using System.Collections.ObjectModel;

#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, 7128, "[iOS] Changing model property scrolls CollectionView back to top",
PlatformAffected.iOS)]
public class Issue7128 : TestNavigationPage
{
protected override void Init()
{
FlagTestHelpers.SetCollectionViewTestFlag();
PushAsync(CreateRoot());
}

class _7128Model : INotifyPropertyChanged
{
private string _url;
private string _text;

public _7128Model(string url, string text)
{
Url = url;
Text = text;
}

public event PropertyChangedEventHandler PropertyChanged;

public string Url
{
get => _url;
set
{
_url = value;
OnPropertyChanged();
}
}

public string Text
{
get => _text;
set
{
_text = value;
OnPropertyChanged();
}
}

protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

View Template()
{
var layout = new Grid();

layout.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
layout.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });

var image = new Image { Aspect = Aspect.AspectFill };
image.SetBinding(Image.SourceProperty, new Binding("Url"));

var label = new Label { Margin = 10, BackgroundColor = Color.Red, HorizontalOptions = LayoutOptions.Fill };
label.SetBinding(Label.TextProperty, new Binding("Text"));

layout.Children.Add(image);
layout.Children.Add(label);
Grid.SetRow(image, 0);
Grid.SetRow(image, 1);

var tapGesture = new TapGestureRecognizer();
tapGesture.Tapped += Tapped;

label.GestureRecognizers.Add(tapGesture);

return layout;
}

void Tapped(object sender, EventArgs e)
{
var label = sender as Label;

var model = (_7128Model)label.BindingContext;

model.Text = DateTime.UtcNow.Millisecond.ToString();
}

Page CreateRoot()
{
var page = new ContentPage() { Title = "Issue7128" };

var layout = new StackLayout() { Padding = 5 };

var instructions = new Label { Text = "Scroll the CollectionView down several pages, then click on one " +
"of the labels. The text of the label should change, but the CollectionView should not scroll to a " +
"different location. If it does scroll, the test has failed."
};

layout.Children.Add(instructions);

var cv = new CollectionView
{
ItemTemplate = new DataTemplate(() => Template())
};

var source = new ObservableCollection<_7128Model>();

var images = new string[] {
"https://raw.githubusercontent.com/xamarin/Xamarin.Forms/master/Xamarin.Forms.ControlGallery.iOS/oasis.jpg",
"https://raw.githubusercontent.com/xamarin/Xamarin.Forms/master/Xamarin.Forms.ControlGallery.iOS/photo.jpg",
"https://raw.githubusercontent.com/xamarin/Xamarin.Forms/master/Xamarin.Forms.ControlGallery.iOS/xamarinstore.jpg",
"https://raw.githubusercontent.com/xamarin/Xamarin.Forms/master/Xamarin.Forms.ControlGallery.iOS/crimson.jpg",
"https://raw.githubusercontent.com/xamarin/Xamarin.Forms/master/Xamarin.Forms.ControlGallery.WindowsUniversal/cover1.jpg"
};

for (int n = 0; n < 35; n++)
{
source.Add(new _7128Model(images[n % 5], $"{n}.jpg"));
}

cv.ItemsSource = source;

layout.Children.Add(cv);

page.Content = layout;

return page;
}
}
}
Expand Up @@ -20,7 +20,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue3475.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue6945.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5046.xaml.cs">
<DependentUpon>Issue5046.xaml</DependentUpon>
<DependentUpon>Issue5046.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue6609.cs" />
Expand All @@ -31,6 +31,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue7049.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue7061.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue7111.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue7128.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellGestures.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellBackButtonBehavior.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellInsets.cs" />
Expand Down Expand Up @@ -1213,7 +1214,6 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
</ItemGroup>

<ItemGroup>
<Folder Include="$(MSBuildThisFileDirectory)Controls\" />
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue2858.xaml">
Expand Down Expand Up @@ -1360,4 +1360,4 @@
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>
</Project>
Expand Up @@ -80,40 +80,26 @@ void UpdateDefaultSupplementaryView(DefaultCell cell, NSString elementKind, NSIn
{
cell.Label.Text = ItemsSource.Group(indexPath).ToString();

if (cell is ItemsViewCell constrainedCell)
if (cell is ItemsViewCell)
{
cell.ConstrainTo(ItemsViewLayout.ConstrainedDimension);
}
}

void UpdateTemplatedSupplementaryView(TemplatedCell cell, NSString elementKind, NSIndexPath indexPath)
{
ApplyTemplateAndDataContext(cell, elementKind, indexPath);
DataTemplate template = elementKind == UICollectionElementKindSectionKey.Header
? GroupableItemsView.GroupHeaderTemplate
: GroupableItemsView.GroupFooterTemplate;

if (cell is ItemsViewCell constrainedCell)
{
cell.ConstrainTo(ItemsViewLayout.ConstrainedDimension);
}
}
var bindingContext = ItemsSource.Group(indexPath);

void ApplyTemplateAndDataContext(TemplatedCell cell, NSString elementKind, NSIndexPath indexPath)
{
DataTemplate template;
cell.Bind(template, bindingContext, ItemsView);

if (elementKind == UICollectionElementKindSectionKey.Header)
if (cell is ItemsViewCell)
{
template = GroupableItemsView.GroupHeaderTemplate;
}
else
{
template = GroupableItemsView.GroupFooterTemplate;
cell.ConstrainTo(ItemsViewLayout.ConstrainedDimension);
}

var templateElement = template.CreateContent() as View;
var renderer = CreateRenderer(templateElement);

BindableObject.SetInheritedBindingContext(renderer.Element, ItemsSource.Group(indexPath));
cell.SetRenderer(renderer);
}

string DetermineViewReuseId(NSString elementKind)
Expand Down
Expand Up @@ -25,5 +25,33 @@ public override void ConstrainTo(CGSize constraint)
ConstrainedDimension = constraint.Height;
Layout(constraint);
}

protected override (bool, Size) NeedsContentSizeUpdate(Size currentSize)
{
var size = Size.Zero;

if (VisualElementRenderer?.Element == null)
{
return (false, size);
}

var bounds = VisualElementRenderer.Element.Bounds;

if (bounds.Width <= 0 || bounds.Height <= 0)
{
return (false, size);
}

var desiredBounds = VisualElementRenderer.Element.Measure(double.PositiveInfinity, bounds.Height,
MeasureFlags.IncludeMargins);

if (desiredBounds.Request.Width == currentSize.Width)
{
// Nothing in the cell needs more room, so leave it as it is
return (false, size);
}

return (true, desiredBounds.Request);
}
}
}
Expand Up @@ -28,5 +28,33 @@ public override void ConstrainTo(CGSize constraint)
ConstrainedDimension = constraint.Height;
Layout(constraint);
}

protected override (bool, Size) NeedsContentSizeUpdate(Size currentSize)
{
var size = Size.Zero;

if (VisualElementRenderer?.Element == null)
{
return (false, size);
}

var bounds = VisualElementRenderer.Element.Bounds;

if (bounds.Width <= 0 || bounds.Height <= 0)
{
return (false, size);
}

var desiredBounds = VisualElementRenderer.Element.Measure(double.PositiveInfinity, bounds.Height,
MeasureFlags.IncludeMargins);

if (desiredBounds.Request.Width == currentSize.Width)
{
// Nothing in the cell needs more room, so leave it as it is
return (false, size);
}

return (true, desiredBounds.Request);
}
}
}

0 comments on commit ed2b0de

Please sign in to comment.