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

[iOS] Fix incorrect refresh indicador position using RefreshView with CollectionView Header #13773

Merged
merged 5 commits into from
May 21, 2021
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,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 @@ -1714,6 +1714,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 @@ -2124,6 +2125,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