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

Commit

Permalink
[iOS] Fix incorrect refresh indicador position using RefreshView with…
Browse files Browse the repository at this point in the history
… CollectionView Header (#13773)

* Added repro sample

* Fix the issue

* Update issue description

* Changes to use another approach to detect if the CollectionView has header or not
  • Loading branch information
jsuarezruiz committed May 21, 2021
1 parent 7644b5b commit 1d0578c
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8" ?>
<controls:TestContentPage
xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
xmlns="http://xamarin.com/schemas/2014/forms"
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.Issue8282"
Title="Issue 8282">
<Grid
RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label
Padding="12"
BackgroundColor="Black"
TextColor="White"
Text="Pull to Refresh. If the loading indicator appears above the CollectionView header, the test has passed. "/>
<Grid
Grid.Row="1">
<RefreshView
BackgroundColor="Yellow"
RefreshColor="Black"
IsRefreshing="{Binding IsRefreshing}"
Command="{Binding RefreshCommand}">
<CollectionView
ItemsSource="{Binding Items}">
<CollectionView.Header>
<Grid
HeightRequest="200"
BackgroundColor="#aadddddd">
<Label
Text="Issue 8282"
HorizontalOptions="Center"
VerticalOptions="Center"/>
</Grid>
</CollectionView.Header>
<CollectionView.ItemsLayout>
<LinearItemsLayout
Orientation="Vertical"
ItemSpacing="5"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid
HeightRequest="100"
BackgroundColor="Beige">
<Label
Text="{Binding Position}"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
</Grid>
</Grid>
</controls:TestContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading.Tasks;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Xaml;

#if UITEST
using Xamarin.UITest;
using Xamarin.UITest.Queries;
using NUnit.Framework;
using Xamarin.Forms.Core.UITests;
#endif

namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[NUnit.Framework.Category(UITestCategories.RefreshView)]
#endif
#if APP
[XamlCompilation(XamlCompilationOptions.Compile)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 8282, "[Bug] [iOS] RefreshView draws behind CollectionView Header", PlatformAffected.iOS)]
public partial class Issue8282 : TestContentPage
{
public Issue8282()
{
#if APP
Title = "Issue 8282";
InitializeComponent();
BindingContext = new Issue8282ViewModel();
#endif
}

protected override void Init()
{

}
}

[Preserve(AllMembers = true)]
public class Issue8282Model : INotifyPropertyChanged
{
private int _position;

public int Position
{
get
{
return _position;
}
set
{
_position = value;

OnPropertyChanged("Position");
}
}

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}

[Preserve(AllMembers = true)]
public class Issue8282ViewModel : INotifyPropertyChanged
{
bool _isRefreshing;

public Issue8282ViewModel()
{
PopulateItems();

RefreshCommand = new Command(async () =>
{
IsRefreshing = true;
await Task.Delay(2000);
PopulateItems();
IsRefreshing = false;
});
}

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

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

OnPropertyChanged("IsRefreshing");
}
}


public Command RefreshCommand { get; set; }

void PopulateItems()
{
var count = Items.Count;

for (var i = count; i < count + 10; i++)
Items.Add(new Issue8282Model() { Position = i });
}

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1717,6 +1717,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue8833.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue10086.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue13136.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8282.xaml.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue13164.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue11924.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue12521.xaml.cs" />
Expand Down Expand Up @@ -2129,6 +2130,9 @@
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue13136.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue8282.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue13376.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace Xamarin.Forms.Platform.iOS
{
public class StructuredItemsViewController<TItemsView> : ItemsViewController<TItemsView>
where TItemsView : StructuredItemsView
public class ItemsViewTags
{
public const int HeaderTag = 111;
public const int FooterTag = 222;
}

public class StructuredItemsViewController<TItemsView> : ItemsViewController<TItemsView>
where TItemsView : StructuredItemsView
{
bool _disposed;

UIView _headerUIView;
Expand Down Expand Up @@ -58,13 +61,13 @@ protected override void Dispose(bool disposing)
protected override CGRect DetermineEmptyViewFrame()
{
nfloat headerHeight = 0;
var headerView = CollectionView.ViewWithTag(HeaderTag);
var headerView = CollectionView.ViewWithTag(ItemsViewTags.HeaderTag);

if (headerView != null)
headerHeight = headerView.Frame.Height;

nfloat footerHeight = 0;
var footerView = CollectionView.ViewWithTag(FooterTag);
var footerView = CollectionView.ViewWithTag(ItemsViewTags.FooterTag);

if (footerView != null)
footerHeight = footerView.Frame.Height;
Expand Down Expand Up @@ -100,14 +103,14 @@ _footerUIView.Frame.Y < (emptyView?.Frame.Y + emptyView?.Frame.Height))

internal void UpdateFooterView()
{
UpdateSubview(ItemsView?.Footer, ItemsView?.FooterTemplate, FooterTag,
UpdateSubview(ItemsView?.Footer, ItemsView?.FooterTemplate, ItemsViewTags.FooterTag,
ref _footerUIView, ref _footerViewFormsElement);
UpdateHeaderFooterPosition();
}

internal void UpdateHeaderView()
{
UpdateSubview(ItemsView?.Header, ItemsView?.HeaderTemplate, HeaderTag,
UpdateSubview(ItemsView?.Header, ItemsView?.HeaderTemplate, ItemsViewTags.HeaderTag,
ref _headerUIView, ref _headerViewFormsElement);
UpdateHeaderFooterPosition();
}
Expand Down
38 changes: 34 additions & 4 deletions Xamarin.Forms.Platform.iOS/Renderers/RefreshViewRenderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,24 @@ bool TryInsertRefresh(UIView view, int index = 0)

if (view is UIScrollView scrollView)
{
if (CanUseRefreshControlProperty())
scrollView.RefreshControl = _refreshControl;
else
scrollView.InsertSubview(_refreshControl, index);
bool addedRefreshControl = false;

if (scrollView is UICollectionView collectionView)
{
if (HasCollectionViewHeader(collectionView))
{
collectionView.BackgroundView = _refreshControl;
addedRefreshControl = true;
}
}

if (!addedRefreshControl)
{
if (CanUseRefreshControlProperty())
scrollView.RefreshControl = _refreshControl;
else
scrollView.InsertSubview(_refreshControl, index);
}

scrollView.AlwaysBounceVertical = true;

Expand Down Expand Up @@ -259,6 +273,22 @@ bool CanUseRefreshControlProperty()
return Forms.IsiOS10OrNewer && !_usingLargeTitles;
}

bool HasCollectionViewHeader(UICollectionView collectionView)
{
bool hasHeader = false;

foreach (var children in collectionView.Subviews)
{
if (children.Tag == ItemsViewTags.HeaderTag)
{
hasHeader = true;
break;
}
}

return hasHeader;
}

void OnRefresh(object sender, EventArgs e)
{
IsRefreshing = true;
Expand Down

0 comments on commit 1d0578c

Please sign in to comment.