[Android/iOS] Fixed ItemsUpdatingScrollMode on CarouselView (#7872)
authorJavier Suárez Ruiz <javiersuarezruiz@hotmail.com>
Thu, 10 Oct 2019 15:31:05 +0000 (17:31 +0200)
committerE.Z. Hart <hartez@users.noreply.github.com>
Thu, 10 Oct 2019 15:31:05 +0000 (09:31 -0600)
* Added repro sample

* Updated sample

* Fixed issue on Android

* Fixed issue on iOS

* Updated sample

* Updated instructions

* Fixed property changed propagation from CarouselViewRenderer to ItemsViewRenderer

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7817.xaml [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7817.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Platform.Android/CollectionView/CarouselViewRenderer.cs
Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewLayout.cs

diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7817.xaml b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7817.xaml
new file mode 100644 (file)
index 0000000..b3357be
--- /dev/null
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<controls:TestContentPage 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"
+             xmlns:controls="clr-namespace:Xamarin.Forms.Controls"  
+             xmlns:local="clr-namespace:Xamarin.Forms.Controls.Issues"
+             mc:Ignorable="d"
+             x:Class="Xamarin.Forms.Controls.Issues.Issue7817">
+    <ContentPage.Content>
+    <Grid
+        Margin="12">
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto" />
+            <RowDefinition Height="Auto" />
+            <RowDefinition Height="*" />
+        </Grid.RowDefinitions>
+        <Grid
+            Grid.Row="0"
+            BackgroundColor="Yellow">
+            <Label
+                LineBreakMode="WordWrap"
+                Text="Change ItemsUpdatingScrollMode by selecting KeepLastItemInView in the Picker and verify that the behavior changes. The CarouselView must move the scroll to the latest item added."/>
+        </Grid>
+        <StackLayout
+            Grid.Row="1"
+            Orientation="Horizontal"
+            HorizontalOptions="Center">
+            <Label
+                Text="UpdatingScrollMode: "
+                VerticalTextAlignment="Center" />
+            <local:EnumPicker x:Name="enumPicker"
+                              EnumType="{x:Type ItemsUpdatingScrollMode}"
+                              SelectedIndex="0"
+                              SelectedIndexChanged="OnItemsUpdatingScrollModeChanged" />
+         </StackLayout>
+        <CarouselView
+            Grid.Row="2"
+            x:Name="carouselView"
+            ItemsSource="{Binding Monkeys}">
+            <CarouselView.ItemTemplate>
+                <DataTemplate>
+                    <StackLayout>
+                        <Frame HasShadow="True"
+                               BorderColor="DarkGray"
+                               CornerRadius="5"
+                               Margin="20"
+                               HeightRequest="300"
+                               HorizontalOptions="Center"
+                               VerticalOptions="CenterAndExpand">
+                            <StackLayout>
+                                <Label Text="{Binding Name}" 
+                                        FontAttributes="Bold"
+                                        FontSize="Large"
+                                        HorizontalOptions="Center"
+                                        VerticalOptions="Center" />
+                                <Image Source="{Binding ImageUrl}" 
+                                        Aspect="AspectFill"
+                                        HeightRequest="150"
+                                        WidthRequest="150"
+                                        HorizontalOptions="Center" />
+                                <Label Text="{Binding Location}"
+                                        HorizontalOptions="Center" />
+                                <Label Text="{Binding Details}"
+                                        FontAttributes="Italic"
+                                        HorizontalOptions="Center"
+                                        MaxLines="5"
+                                        LineBreakMode="TailTruncation" />
+                            </StackLayout>
+                        </Frame>
+                    </StackLayout>
+                </DataTemplate>
+            </CarouselView.ItemTemplate>
+        </CarouselView>
+    </Grid>
+    </ContentPage.Content>
+</controls:TestContentPage>
\ No newline at end of file
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7817.xaml.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue7817.xaml.cs
new file mode 100644 (file)
index 0000000..d981744
--- /dev/null
@@ -0,0 +1,162 @@
+using System;
+using System.Collections.ObjectModel;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Xaml;
+using System.Reflection;
+
+#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
+       [Category(UITestCategories.CarouselView)]
+#endif
+#if APP
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+#endif
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.Github, 7817, "[Android/iOS] Changing ItemsUpdatingScrollMode has no effect on CarouselView")]
+       public partial class Issue7817 : TestContentPage
+       {
+               public Issue7817()
+               {
+#if APP
+                       Device.SetFlags(new List<string> { CollectionView.CollectionViewExperimental });
+                       Title = "Issue 7817";
+                       InitializeComponent();
+#endif
+               }
+
+               protected override async void Init()
+               {
+                       BindingContext = new Issue7817ViewModel();
+                       await ((Issue7817ViewModel)BindingContext).CreateCollectionAsync();
+               }
+
+               void OnItemsUpdatingScrollModeChanged(object sender, EventArgs e)
+               {
+#if APP
+                       carouselView.ItemsUpdatingScrollMode = (ItemsUpdatingScrollMode)(sender as EnumPicker).SelectedItem;
+#endif
+               }
+       }
+
+       [Preserve(AllMembers = true)]
+       public class EnumPicker : Picker
+       {
+               public static readonly BindableProperty EnumTypeProperty = BindableProperty.Create(nameof(EnumType), typeof(Type), typeof(EnumPicker),
+                       propertyChanged: (bindable, oldValue, newValue) =>
+                       {
+                               var picker = (EnumPicker)bindable;
+
+                               if (oldValue != null)
+                               {
+                                       picker.ItemsSource = null;
+                               }
+                               if (newValue != null)
+                               {
+                                       if (!((Type)newValue).GetTypeInfo().IsEnum)
+                                               throw new ArgumentException("EnumPicker: EnumType property must be enumeration type");
+
+                                       picker.ItemsSource = Enum.GetValues((Type)newValue);
+                               }
+                       });
+
+               public Type EnumType
+               {
+                       set => SetValue(EnumTypeProperty, value);
+                       get => (Type)GetValue(EnumTypeProperty);
+               }
+       }
+
+       [Preserve(AllMembers = true)]
+       public class Issue7817Model
+       {
+               public int Index { get; set; }
+               public string Name { get; set; }
+               public string Location { get; set; }
+               public string Details { get; set; }
+               public string ImageUrl { get; set; }
+       }
+
+       [Preserve(AllMembers = true)]
+       public class Issue7817ViewModel : BindableObject
+       {
+               const int AddItemDelay = 2000;
+
+               public ObservableCollection<Issue7817Model> Monkeys { get; private set; } = new ObservableCollection<Issue7817Model>();
+
+               public async Task CreateCollectionAsync()
+               {
+                       Monkeys.Add(new Issue7817Model
+                       {
+                               Index = 0,
+                               Name = "Baboon",
+                               Location = "Africa & Asia",
+                               Details = "Baboons are African and Arabian Old World monkeys belonging to the genus Papio, part of the subfamily Cercopithecinae.",
+                               ImageUrl = "http://upload.wikimedia.org/wikipedia/commons/thumb/f/fc/Papio_anubis_%28Serengeti%2C_2009%29.jpg/200px-Papio_anubis_%28Serengeti%2C_2009%29.jpg"
+                       });
+
+                       await Task.Delay(AddItemDelay);
+
+                       Monkeys.Add(new Issue7817Model
+                       {
+                               Index = 1,
+                               Name = "Capuchin Monkey",
+                               Location = "Central & South America",
+                               Details = "The capuchin monkeys are New World monkeys of the subfamily Cebinae. Prior to 2011, the subfamily contained only a single genus, Cebus.",
+                               ImageUrl = "http://upload.wikimedia.org/wikipedia/commons/thumb/4/40/Capuchin_Costa_Rica.jpg/200px-Capuchin_Costa_Rica.jpg"
+                       });
+
+                       await Task.Delay(AddItemDelay);
+
+                       Monkeys.Add(new Issue7817Model
+                       {
+                               Index = 2,
+                               Name = "Blue Monkey",
+                               Location = "Central and East Africa",
+                               Details = "The blue monkey or diademed monkey is a species of Old World monkey native to Central and East Africa, ranging from the upper Congo River basin east to the East African Rift and south to northern Angola and Zambia",
+                               ImageUrl = "http://upload.wikimedia.org/wikipedia/commons/thumb/8/83/BlueMonkey.jpg/220px-BlueMonkey.jpg"
+                       });
+
+                       Monkeys.Add(new Issue7817Model
+                       {
+                               Index = 3,
+                               Name = "Thomas's Langur",
+                               Location = "Indonesia",
+                               Details = "Thomas's langur is a species of primate in the family Cercopithecidae. It is endemic to North Sumatra, Indonesia. Its natural habitat is subtropical or tropical dry forests. It is threatened by habitat loss. Its native names are reungkah in Acehnese and kedih in Alas.",
+                               ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/3/31/Thomas%27s_langur_Presbytis_thomasi.jpg/142px-Thomas%27s_langur_Presbytis_thomasi.jpg"
+                       });
+
+                       await Task.Delay(AddItemDelay);
+
+                       Monkeys.Add(new Issue7817Model
+                       {
+                               Index = 4,
+                               Name = "Purple-faced Langur",
+                               Location = "Sri Lanka",
+                               Details = "The purple-faced langur, also known as the purple-faced leaf monkey, is a species of Old World monkey that is endemic to Sri Lanka. The animal is a long-tailed arboreal species, identified by a mostly brown appearance, dark face (with paler lower face) and a very shy nature. The species was once highly prevalent, found in suburban Colombo and the \"wet zone\" villages (areas with high temperatures and high humidity throughout the year, whilst rain deluges occur during the monsoon seasons), but rapid urbanization has led to a significant decrease in the population level of the monkeys.",
+                               ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Semnopithèque_blanchâtre_mâle.JPG/192px-Semnopithèque_blanchâtre_mâle.JPG"
+                       });
+
+                       await Task.Delay(AddItemDelay);
+
+                       Monkeys.Add(new Issue7817Model
+                       {
+                               Index = 5,
+                               Name = "Gelada",
+                               Location = "Ethiopia",
+                               Details = "The gelada, sometimes called the bleeding-heart monkey or the gelada baboon, is a species of Old World monkey found only in the Ethiopian Highlands, with large populations in the Semien Mountains. Theropithecus is derived from the Greek root words for \"beast-ape.\" Like its close relatives the baboons, it is largely terrestrial, spending much of its time foraging in grasslands.",
+                               ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/1/13/Gelada-Pavian.jpg/320px-Gelada-Pavian.jpg"
+                       });
+               }
+       }
+}
\ No newline at end of file
index 6dc6cac..05aaefd 100644 (file)
@@ -65,6 +65,9 @@
     <Compile Include="$(MSBuildThisFileDirectory)Issue7789.xaml.cs">
       <SubType>Code</SubType>
     </Compile>
+    <Compile Include="$(MSBuildThisFileDirectory)Issue7817.xaml.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="$(MSBuildThisFileDirectory)RefreshViewTests.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue7338.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)ScrollToGroup.cs" />
     <Compile Update="$(MSBuildThisFileDirectory)Issue7789.xaml.cs">
       <DependentUpon>Issue7789.xaml</DependentUpon>
     </Compile>
+    <Compile Update="$(MSBuildThisFileDirectory)Issue7519Xaml.xaml.cs">      
+      <DependentUpon>Issue7519Xaml.xaml</DependentUpon>
+    </Compile>
+    <Compile Update="$(MSBuildThisFileDirectory)Issue7817.xaml.cs">
+      <DependentUpon>Issue7817.xaml</DependentUpon>
+    </Compile>
     <Compile Update="$(MSBuildThisFileDirectory)Issue7519Xaml.xaml.cs">
       <DependentUpon>Issue7519Xaml.xaml</DependentUpon>
     </Compile>
       <Generator>MSBuild:Compile</Generator>
     </EmbeddedResource>
   </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue7817.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </EmbeddedResource>
+  </ItemGroup>
 </Project>
\ No newline at end of file
index f827aee..7b57653 100644 (file)
@@ -54,9 +54,9 @@ namespace Xamarin.Forms.Platform.Android
 
                protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs changedProperty)
                {
-                       if (changedProperty.Is(ItemsView.ItemsSourceProperty))
-                               UpdateItemsSource();
-                       else if (changedProperty.Is(CarouselView.PeekAreaInsetsProperty))
+                       base.OnElementPropertyChanged(sender, changedProperty);
+   
+                       if (changedProperty.Is(CarouselView.PeekAreaInsetsProperty))
                                UpdatePeekAreaInsets();
                        else if (changedProperty.Is(CarouselView.IsSwipeEnabledProperty))
                                UpdateIsSwipeEnabled();
index c3198e5..74fad4a 100644 (file)
@@ -484,7 +484,7 @@ namespace Xamarin.Forms.Platform.iOS
 
                        if (ItemsUpdatingScrollMode == ItemsUpdatingScrollMode.KeepLastItemInView)
                        {
-                               ForceScrollToLastItem(CollectionView);
+                               ForceScrollToLastItem(CollectionView, _itemsLayout);
                        }
                }
 
@@ -551,7 +551,7 @@ namespace Xamarin.Forms.Platform.iOS
                        return false;
                }
 
-               static void ForceScrollToLastItem(UICollectionView collectionView)
+               static void ForceScrollToLastItem(UICollectionView collectionView, ItemsLayout itemsLayout)
                {
                        var sections = (int)collectionView.NumberOfSections();
 
@@ -566,7 +566,12 @@ namespace Xamarin.Forms.Platform.iOS
                                if (itemCount > 0)
                                {
                                        var lastIndexPath = NSIndexPath.FromItemSection(itemCount - 1, section);
-                                       collectionView.ScrollToItem(lastIndexPath, UICollectionViewScrollPosition.Bottom, true);
+
+                                       if (itemsLayout.Orientation == ItemsLayoutOrientation.Vertical)
+                                               collectionView.ScrollToItem(lastIndexPath, UICollectionViewScrollPosition.Bottom, true);
+                                       else
+                                               collectionView.ScrollToItem(lastIndexPath, UICollectionViewScrollPosition.Right, true);
+
                                        return;
                                }
                        }