Allow CollectionView items to resize with their content (#5905)
authorE.Z. Hart <hartez@users.noreply.github.com>
Wed, 24 Apr 2019 18:30:02 +0000 (12:30 -0600)
committerSamantha Houts <samhouts@users.noreply.github.com>
Wed, 24 Apr 2019 18:30:02 +0000 (11:30 -0700)
* Add gallery for reproing expanding DataTemplate content;
Allow ItemContentView on Android to expand with content;
Allow ItemContentView on iOS to expand with content;
Fixes #5647

* Improve instructions
Fixes #5721
Fixes #5521

* Apply suggestions from code review

Co-Authored-By: hartez <hartez@users.noreply.github.com>
* Clear binding contexts before recycling cells/viewholders.

16 files changed:
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CollectionViewGallery.cs
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/CollectionViewGalleryTestItem.cs
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/DataTemplateGallery.cs
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ExampleTemplates.cs
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ItemSizeGalleries/DynamicItemSizeGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ItemSizeGalleries/ItemsSizeGallery.cs [new file with mode: 0644]
Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ItemSizeGalleries/VariableSizeTemplateGridGallery.cs [moved from Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/VariableSizeTemplateGridGallery.cs with 98% similarity]
Xamarin.Forms.Platform.Android/CollectionView/ItemContentView.cs
Xamarin.Forms.Platform.Android/CollectionView/TemplatedItemViewHolder.cs
Xamarin.Forms.Platform.iOS/CollectionView/HorizontalTemplatedCell.cs
Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewCell.cs
Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewController.cs
Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewLayout.cs
Xamarin.Forms.Platform.iOS/CollectionView/TemplatedCell.cs
Xamarin.Forms.Platform.iOS/CollectionView/UICollectionViewDelegator.cs
Xamarin.Forms.Platform.iOS/CollectionView/VerticalTemplatedCell.cs

index f834f3b..070ce57 100644 (file)
@@ -1,5 +1,6 @@
 using Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.EmptyViewGalleries;
 using Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.SelectionGalleries;
+using Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.ItemSizeGalleries;
 
 namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
 {
@@ -21,6 +22,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                                        GalleryBuilder.NavButton("EmptyView Galleries", () => new EmptyViewGallery(), Navigation),
                                        GalleryBuilder.NavButton("Selection Galleries", () => new SelectionGallery(), Navigation),
                                        GalleryBuilder.NavButton("Propagation Galleries", () => new PropagationGallery(), Navigation),
+                                       GalleryBuilder.NavButton("Item Size Galleries", () => new ItemsSizeGallery(), Navigation),
                                }
                        };
                }
index 20df727..2778471 100644 (file)
@@ -1,15 +1,33 @@
 using System;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Windows.Input;
 using Xamarin.Forms.Internals;
 
 namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
 {
        [Preserve(AllMembers = true)]
-       public class CollectionViewGalleryTestItem
+       public class CollectionViewGalleryTestItem : INotifyPropertyChanged
        {
+               string _caption;
+
                public DateTime Date { get; set; }
-               public string Caption { get; set; }
+
+               public string Caption
+               {
+                       get => _caption;
+                       set
+                       {
+                               _caption = value;
+                               OnPropertyChanged();
+                       }
+               }
+
                public string Image { get; set; }
+
                public int Index { get; set; }
+               public ICommand MoreCommand { get; set; }
+               public ICommand LessCommand { get; set; }
 
                public CollectionViewGalleryTestItem(DateTime date, string caption, string image, int index)
                {
@@ -17,11 +35,31 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                        Caption = caption;
                        Image = image;
                        Index = index;
+
+                       var text = " Lorem ipsum dolor sit amet, qui eleifend adversarium ei, pro tamquam pertinax inimicus ut. Quis assentior ius no, ne vel modo tantas omnium, sint labitur id nec. Mel ad cetero repudiare definiebas, eos sint placerat cu.";
+
+                       MoreCommand = new Command(() => Caption += text);
+                       LessCommand = new Command(() =>
+                       {
+                               var last = Caption.LastIndexOf(text);
+                               if (last > 0)
+                               {
+                                       Caption = Caption.Substring(0, last);
+                               }
+
+                       });
                }
 
+               public event PropertyChangedEventHandler PropertyChanged;
+
                public override string ToString()
                {
                        return $"Item: {Index}";
                }
+
+               protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
+               {
+                       PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+               }
        }
-}
\ No newline at end of file
+}
index 8070cbd..48791e6 100644 (file)
                                                        new TemplateCodeCollectionViewGridGallery (), Navigation),
                                                GalleryBuilder.NavButton("Horizontal Grid (Code)", () => 
                                                        new TemplateCodeCollectionViewGridGallery (ItemsLayoutOrientation.Horizontal), Navigation),
-
-                                               GalleryBuilder.NavButton("ItemSizing Strategy", () => 
-                                                       new VariableSizeTemplateGridGallery (ItemsLayoutOrientation.Horizontal), Navigation),
-
                         GalleryBuilder.NavButton("DataTemplateSelector", () =>
                             new DataTemplateSelectorGallery(), Navigation),
                                        }
index 5bfeaba..076c594 100644 (file)
@@ -18,7 +18,8 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
 
                                var image = new Image
                                {
-                                       HeightRequest = 100, WidthRequest = 100,
+                                       HeightRequest = 100,
+                                       WidthRequest = 100,
                                        HorizontalOptions = LayoutOptions.Center,
                                        VerticalOptions = LayoutOptions.Center,
                                        Margin = new Thickness(2, 5, 2, 2),
@@ -36,7 +37,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                                };
 
                                caption.SetBinding(Label.TextProperty, new Binding("Caption"));
-                               
+
                                templateLayout.Children.Add(image);
                                templateLayout.Children.Add(caption);
 
@@ -52,7 +53,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                        {
                                var templateLayout = new Grid
                                {
-                                       RowDefinitions = new RowDefinitionCollection { new RowDefinition(), new RowDefinition {Height = GridLength.Auto} },
+                                       RowDefinitions = new RowDefinitionCollection { new RowDefinition(), new RowDefinition { Height = GridLength.Auto } },
                                        WidthRequest = 280,
                                        HeightRequest = 310,
                                };
@@ -78,7 +79,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                                };
 
                                caption.SetBinding(Label.TextProperty, new Binding("Caption"));
-                               
+
                                templateLayout.Children.Add(image);
                                templateLayout.Children.Add(caption);
 
@@ -120,7 +121,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                                };
 
                                caption.SetBinding(Label.TextProperty, new Binding("Caption"));
-                               
+
                                grid.Children.Add(image);
                                grid.Children.Add(caption);
 
@@ -132,7 +133,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                                        Content = grid
                                };
 
-                               return  frame;
+                               return frame;
                        });
                }
 
@@ -166,12 +167,13 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                                {
                                        HorizontalOptions = LayoutOptions.Fill,
                                        HorizontalTextAlignment = TextAlignment.Center,
-                                       HeightRequest = 40, WidthRequest = 100,
+                                       HeightRequest = 40,
+                                       WidthRequest = 100,
                                        BackgroundColor = Color.Crimson,
                                        Text = "Caption"
                                };
 
-                               caption.SetBinding(Label.TextProperty, new Binding("Index", stringFormat:"Index {0}"));
+                               caption.SetBinding(Label.TextProperty, new Binding("Index", stringFormat: "Index {0}"));
 
                                templateLayout.Children.Add(image);
                                templateLayout.Children.Add(caption);
@@ -190,7 +192,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                                {
                                        BackgroundColor = Color.Bisque,
 
-                                       RowDefinitions = new RowDefinitionCollection { new RowDefinition(), new RowDefinition {Height = GridLength.Auto} },
+                                       RowDefinitions = new RowDefinitionCollection { new RowDefinition(), new RowDefinition { Height = GridLength.Auto } },
                                        WidthRequest = 100,
                                        HeightRequest = 140
                                };
@@ -211,13 +213,14 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                                {
                                        HorizontalOptions = LayoutOptions.Fill,
                                        HorizontalTextAlignment = TextAlignment.Center,
-                                       HeightRequest = 40, WidthRequest = 100,
+                                       HeightRequest = 40,
+                                       WidthRequest = 100,
                                        BackgroundColor = Color.Crimson,
                                        Text = "Caption"
                                };
 
                                caption.SetBinding(Label.TextProperty, new Binding("Date", stringFormat: "{0:d}"));
-                               
+
                                templateLayout.Children.Add(image);
                                templateLayout.Children.Add(caption);
 
@@ -268,7 +271,7 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                                return templateLayout;
                        });
                }
-               
+
                public static DataTemplate VariableSizeTemplate()
                {
                        var indexHeightConverter = new IndexRequestConverter(3, 50, 150);
@@ -299,6 +302,93 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                        });
                }
 
+               public static DataTemplate DynamicTextTemplate()
+               {
+                       return new DataTemplate(() =>
+                       {
+                               var templateLayout = new Grid
+                               {
+                                       RowDefinitions = new RowDefinitionCollection
+                                       {
+                                               new RowDefinition() { Height = GridLength.Auto },
+                                               new RowDefinition() { Height = GridLength.Auto },
+                                               new RowDefinition() { Height = GridLength.Auto }
+                                       },
+                                       BackgroundColor = Color.LightGoldenrodYellow,
+                                       Margin = 10
+                               };
+
+                               var frame = new Frame
+                               {
+                                       HeightRequest = 50,
+                                       WidthRequest = 200,
+                                       HorizontalOptions = LayoutOptions.Center,
+                                       VerticalOptions = LayoutOptions.Center,
+                                       Margin = new Thickness(2, 5, 2, 2),
+                                       AutomationId = "frame",
+                                       BackgroundColor = Color.CadetBlue
+                               };
+
+                               var date = new Label
+                               {
+                                       FontSize = 12,
+                                       HorizontalOptions = LayoutOptions.Fill,
+                                       VerticalOptions = LayoutOptions.Fill,
+                                       HorizontalTextAlignment = TextAlignment.Center,
+                                       VerticalTextAlignment = TextAlignment.Center,
+                                       Margin = new Thickness(2, 0, 2, 2)
+                               };
+
+                               date.SetBinding(Label.TextProperty, new Binding("Date"));
+
+                               frame.Content = date;
+
+                               var caption = new Label
+                               {
+                                       FontSize = 12,
+                                       HorizontalOptions = LayoutOptions.Fill,
+                                       HorizontalTextAlignment = TextAlignment.Center,
+                                       Margin = new Thickness(2, 0, 2, 2),
+                                       LineBreakMode = LineBreakMode.WordWrap,
+                                       MaxLines = 10
+                               };
+
+                               caption.SetBinding(Label.TextProperty, new Binding("Caption"));
+
+                               var more = new Button { Text = "More Text" };
+                               var less = new Button { Text = "Less Text" };
+
+                               var buttonLayout = new StackLayout
+                               {
+                                       Children = { more, less },
+                                       Orientation = StackOrientation.Horizontal,
+                                       HorizontalOptions = LayoutOptions.Center                                        
+                               };
+
+                               more.SetBinding(Button.CommandProperty, new Binding("MoreCommand"));
+                               less.SetBinding(Button.CommandProperty, new Binding("LessCommand"));
+
+                               templateLayout.Children.Add(frame);
+                               templateLayout.Children.Add(caption);
+                               templateLayout.Children.Add(buttonLayout);
+
+                               Grid.SetRow(buttonLayout, 1);
+                               Grid.SetRow(caption, 2);
+
+                               var rootLayout = new StackLayout
+                               {
+                                       Children = { templateLayout }
+                               };
+
+                               return rootLayout;
+                       });
+               }
+
+               static void More_Clicked(object sender, EventArgs e)
+               {
+                       throw new NotImplementedException();
+               }
+
                class IndexRequestConverter : IValueConverter
                {
                        readonly int _cutoff;
@@ -335,4 +425,4 @@ namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries
                        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
                }
        }
-}
\ No newline at end of file
+}
diff --git a/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ItemSizeGalleries/DynamicItemSizeGallery.cs b/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ItemSizeGalleries/DynamicItemSizeGallery.cs
new file mode 100644 (file)
index 0000000..5e63cf6
--- /dev/null
@@ -0,0 +1,45 @@
+namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.ItemSizeGalleries
+{
+       internal class DynamicItemSizeGallery : ContentPage
+       {
+               public DynamicItemSizeGallery(IItemsLayout itemsLayout)
+               {
+                       var layout = new Grid
+                       {
+                               RowDefinitions = new RowDefinitionCollection
+                               {
+                                       new RowDefinition { Height = GridLength.Auto },
+                                       new RowDefinition { Height = GridLength.Auto },
+                                       new RowDefinition { Height = GridLength.Star }
+                               }
+                       };
+
+                       var instructions = new Label
+                       {
+                               Text = "Tap the buttons in each item to increase/decrease the amount of text. The items should expand and contract to accommodate the text."
+                       };
+
+                       var itemTemplate = ExampleTemplates.DynamicTextTemplate();
+
+                       var collectionView = new CollectionView
+                       {
+                               ItemsLayout = itemsLayout,
+                               ItemTemplate = itemTemplate,
+                               AutomationId = "collectionview"
+                       };
+
+                       var generator = new ItemsSourceGenerator(collectionView, initialItems: 20);
+
+                       layout.Children.Add(generator);
+                       layout.Children.Add(instructions);
+                       layout.Children.Add(collectionView);
+
+                       Grid.SetRow(instructions, 1);
+                       Grid.SetRow(collectionView, 2);
+
+                       Content = layout;
+
+                       generator.GenerateItems();
+               }
+       }
+}
diff --git a/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ItemSizeGalleries/ItemsSizeGallery.cs b/Xamarin.Forms.Controls/GalleryPages/CollectionViewGalleries/ItemSizeGalleries/ItemsSizeGallery.cs
new file mode 100644 (file)
index 0000000..4442bb6
--- /dev/null
@@ -0,0 +1,30 @@
+namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.ItemSizeGalleries
+{
+       internal class ItemsSizeGallery : ContentPage
+       {
+               public ItemsSizeGallery()
+               {
+                       var descriptionLabel =
+                               new Label { Text = "Item Size Galleries", Margin = new Thickness(2, 2, 2, 2) };
+
+                       Title = "Item Size Galleries";
+
+                       Content = new ScrollView
+                       {
+                               Content = new StackLayout
+                               {
+                                       Children =
+                                       {
+                                               descriptionLabel,
+                                               GalleryBuilder.NavButton("Expanding Text (Vertical List)", () =>
+                                                       new DynamicItemSizeGallery(ListItemsLayout.VerticalList), Navigation),
+                                               GalleryBuilder.NavButton("Expanding Text (Horizontal List)", () =>
+                                                       new DynamicItemSizeGallery(ListItemsLayout.HorizontalList), Navigation),
+                                               GalleryBuilder.NavButton("ItemSizing Strategy", () =>
+                                                       new VariableSizeTemplateGridGallery (ItemsLayoutOrientation.Horizontal), Navigation)
+                                       }
+                               }
+                       };
+               }
+       }
+}
index a6f6d9e..e38da87 100644 (file)
@@ -1,4 +1,3 @@
-using System;
 using Android.Content;
 using Android.Views;
 
@@ -16,10 +15,17 @@ namespace Xamarin.Forms.Platform.Android
                {
                        Content = CreateRenderer(view, Context);
                        AddView(Content.View);
+                       Content.Element.MeasureInvalidated += ElementMeasureInvalidated;
+               }
+
+               void ElementMeasureInvalidated(object sender, System.EventArgs e)
+               {
+                       RequestLayout();
                }
 
                internal void Recycle()
                {
+                       Content.Element.MeasureInvalidated -= ElementMeasureInvalidated;
                        RemoveView(Content.View);
                        Content = null;
                }
@@ -54,18 +60,17 @@ namespace Xamarin.Forms.Platform.Android
 
                        SizeRequest measure = Content.Element.Measure(width, height, MeasureFlags.IncludeMargins);
 
+                       // When we implement ItemSizingStrategy.MeasureFirstItem for Android, these next two clauses will need to
+                       // be updated to use the static width/height
+
                        if (pixelWidth == 0)
                        {
-                               pixelWidth = (int)Context.ToPixels(Content.Element.Width > 0
-                                       ? Content.Element.Width
-                                       : measure.Request.Width);
+                               pixelWidth = (int)Context.ToPixels(measure.Request.Width);
                        }
 
                        if (pixelHeight == 0)
                        {
-                               pixelHeight = (int)Context.ToPixels(Content.Element.Height > 0
-                                       ? Content.Element.Height
-                                       : measure.Request.Height);
+                               pixelHeight = (int)Context.ToPixels(measure.Request.Height);
                        }
 
                        SetMeasuredDimension(pixelWidth, pixelHeight);
@@ -73,20 +78,10 @@ namespace Xamarin.Forms.Platform.Android
 
                static IVisualElementRenderer CreateRenderer(View view, Context context)
                {
-                       if (view == null)
-                       {
-                               throw new ArgumentNullException(nameof(view));
-                       }
-
-                       if (context == null)
-                       {
-                               throw new ArgumentNullException(nameof(context));
-                       }
-
                        var renderer = Platform.CreateRenderer(view, context);
                        Platform.SetRenderer(view, renderer);
 
                        return renderer;
                }
        }
-}
\ No newline at end of file
+}
index 19dbf89..f5e579b 100644 (file)
@@ -1,13 +1,12 @@
 using System;
-using Android.Content;
 using Xamarin.Forms.Internals;
 
 namespace Xamarin.Forms.Platform.Android
 {
        internal class TemplatedItemViewHolder : SelectableViewHolder
        {
-               private readonly ItemContentView _itemContentView;
-               private readonly DataTemplate _template;
+               readonly ItemContentView _itemContentView;
+               readonly DataTemplate _template;
 
                public View View { get; private set; }
 
@@ -33,6 +32,7 @@ namespace Xamarin.Forms.Platform.Android
 
                public void Recycle(ItemsView itemsView)
                {
+                       View.BindingContext = null;
                        itemsView.RemoveLogicalChild(View);
                        _itemContentView.Recycle();
                }
index c47ac9d..5c58eb6 100644 (file)
@@ -1,7 +1,5 @@
-using System;
-using CoreGraphics;
+using CoreGraphics;
 using Foundation;
-using UIKit;
 
 namespace Xamarin.Forms.Platform.iOS
 {
@@ -19,10 +17,7 @@ namespace Xamarin.Forms.Platform.iOS
                        var measure = VisualElementRenderer.Element.Measure(double.PositiveInfinity, 
                                ConstrainedDimension, MeasureFlags.IncludeMargins);
 
-                       var width = VisualElementRenderer.Element.Width > 0 
-                               ? VisualElementRenderer.Element.Width : measure.Request.Width;
-
-                       return new CGSize(width, ConstrainedDimension);
+                       return new CGSize(measure.Request.Height, ConstrainedDimension);
                }
 
                public override void ConstrainTo(CGSize constraint)
index 8ee922f..31d1b44 100644 (file)
@@ -23,8 +23,18 @@ namespace Xamarin.Forms.Platform.iOS
 
                protected void InitializeContentConstraints(UIView nativeView)
                {
-                       ContentView.AddSubview(nativeView);
                        ContentView.TranslatesAutoresizingMaskIntoConstraints = false;
+                       nativeView.TranslatesAutoresizingMaskIntoConstraints = false;
+
+                       ContentView.AddSubview(nativeView);
+
+                       // We want the cell to be the same size as the ContentView
+                       ContentView.TopAnchor.ConstraintEqualTo(TopAnchor).Active = true;
+                       ContentView.BottomAnchor.ConstraintEqualTo(BottomAnchor).Active = true;
+                       ContentView.LeadingAnchor.ConstraintEqualTo(LeadingAnchor).Active = true;
+                       ContentView.TrailingAnchor.ConstraintEqualTo(TrailingAnchor).Active = true;
+
+                       // And we want the ContentView to be the same size as the root renderer for the Forms element
                        ContentView.TopAnchor.ConstraintEqualTo(nativeView.TopAnchor).Active = true;
                        ContentView.BottomAnchor.ConstraintEqualTo(nativeView.BottomAnchor).Active = true;
                        ContentView.LeadingAnchor.ConstraintEqualTo(nativeView.LeadingAnchor).Active = true;
index cdced0a..ebe06c5 100644 (file)
@@ -244,17 +244,29 @@ namespace Xamarin.Forms.Platform.iOS
 
                        // And make sure it's a "child" of the ItemsView
                        _itemsView.AddLogicalChild(view);
+
+                       cell.ContentSizeChanged += CellContentSizeChanged;
+               }
+
+               void CellContentSizeChanged(object sender, EventArgs e)
+               {
+                       Layout?.InvalidateLayout();
                }
 
-               internal void RemoveLogicalChild(UICollectionViewCell cell)
+               internal void PrepareCellForRemoval(UICollectionViewCell cell)
                {
                        if (cell is TemplatedCell templatedCell)
                        {
+                               templatedCell.ContentSizeChanged -= CellContentSizeChanged;
+
                                var oldView = templatedCell.VisualElementRenderer?.Element;
                                if (oldView != null)
                                {
+                                       oldView.BindingContext = null;
                                        _itemsView.RemoveLogicalChild(oldView);
                                }
+
+                               templatedCell.PrepareForRemoval();
                        }
                }
 
index 5ad9021..dccc76d 100644 (file)
@@ -122,6 +122,19 @@ namespace Xamarin.Forms.Platform.iOS
                        return shouldInvalidate;
                }
 
+               public override bool ShouldInvalidateLayout(UICollectionViewLayoutAttributes preferredAttributes, UICollectionViewLayoutAttributes originalAttributes)
+               {
+                       if (ItemSizingStrategy == ItemSizingStrategy.MeasureAllItems)
+                       {
+                               if (preferredAttributes.Bounds != originalAttributes.Bounds)
+                               {
+                                       return true;
+                               }
+                       }
+
+                       return base.ShouldInvalidateLayout(preferredAttributes, originalAttributes);
+               }
+
                protected void DetermineCellSize()
                {
                        if (GetPrototype == null)
index ce51af0..5c2cd05 100644 (file)
@@ -5,9 +5,9 @@ using UIKit;
 
 namespace Xamarin.Forms.Platform.iOS
 {
-       // TODO hartez 2018/09/17 14:11:02 Should this be named "TemplateCell" instead of "TemplatedCell"?      
        public abstract class TemplatedCell : ItemsViewCell
        {
+               public event EventHandler<EventArgs> ContentSizeChanged;
                protected nfloat ConstrainedDimension;
 
                [Export("initWithFrame:")]
@@ -25,26 +25,38 @@ namespace Xamarin.Forms.Platform.iOS
                public override UICollectionViewLayoutAttributes PreferredLayoutAttributesFittingAttributes(
                        UICollectionViewLayoutAttributes layoutAttributes)
                {
-                       var nativeView = VisualElementRenderer.NativeView;
+                       var preferredAttributes = base.PreferredLayoutAttributesFittingAttributes(layoutAttributes);
 
+                       // Measure this cell (including the Forms element)
                        var size = Measure();
 
+                       // Update the size of the root view to accommodate the Forms element
+                       var nativeView = VisualElementRenderer.NativeView;
                        nativeView.Frame = new CGRect(CGPoint.Empty, size);
+
+                       // Layout the Forms element 
                        VisualElementRenderer.Element.Layout(nativeView.Frame.ToRectangle());
 
-                       layoutAttributes.Frame = nativeView.Frame;
+                       // Adjust the preferred attributes to include space for the Forms element
+                       preferredAttributes.Frame = new CGRect(preferredAttributes.Frame.Location, size);
 
-                       return layoutAttributes;
+                       return preferredAttributes;
                }
 
-               public void SetRenderer(IVisualElementRenderer renderer)
+               public override void PrepareForReuse()
                {
+                       base.PrepareForReuse();
                        ClearSubviews();
+               }
 
+               public void SetRenderer(IVisualElementRenderer renderer)
+               {
                        VisualElementRenderer = renderer;
                        var nativeView = VisualElementRenderer.NativeView;
 
                        InitializeContentConstraints(nativeView);
+
+                       renderer.Element.MeasureInvalidated += MeasureInvalidated;
                }
 
                protected void Layout(CGSize constraints)
@@ -65,7 +77,6 @@ namespace Xamarin.Forms.Platform.iOS
                {
                        for (int n = ContentView.Subviews.Length - 1; n >= 0; n--)
                        {
-                               // TODO hartez 2018/09/07 16:14:43 Does this also need to clear the constraints?        
                                ContentView.Subviews[n].RemoveFromSuperview();
                        }
                }
@@ -81,11 +92,41 @@ namespace Xamarin.Forms.Platform.iOS
 
                                if (element != null)
                                {
-                                       VisualStateManager.GoToState(element, value 
-                                               ? VisualStateManager.CommonStates.Selected 
+                                       VisualStateManager.GoToState(element, value
+                                               ? VisualStateManager.CommonStates.Selected
                                                : VisualStateManager.CommonStates.Normal);
                                }
                        }
                }
+
+               void MeasureInvalidated(object sender, EventArgs args)
+               {
+                       if (VisualElementRenderer?.Element == null)
+                       {
+                               return;
+                       }
+
+                       var bounds = VisualElementRenderer.Element.Bounds;
+
+                       if (bounds.Width <= 0 || bounds.Height <= 0)
+                       {
+                               return;
+                       }
+
+                       OnContentSizeChanged();
+               }
+
+               public void PrepareForRemoval()
+               {
+                       if (VisualElementRenderer?.Element != null)
+                       {
+                               VisualElementRenderer.Element.MeasureInvalidated -= MeasureInvalidated;
+                       }
+               }
+
+               protected void OnContentSizeChanged()
+               {
+                       ContentSizeChanged?.Invoke(this, EventArgs.Empty);
+               }
        }
 }
\ No newline at end of file
index 07bbabd..d654792 100644 (file)
@@ -69,7 +69,7 @@ namespace Xamarin.Forms.Platform.iOS
 
                public override void CellDisplayingEnded(UICollectionView collectionView, UICollectionViewCell cell, NSIndexPath indexPath)
                {
-                       ItemsViewController.RemoveLogicalChild(cell);
+                       ItemsViewController.PrepareCellForRemoval(cell);
                }
        }
 }
\ No newline at end of file
index a4927fa..6acbab7 100644 (file)
@@ -1,8 +1,5 @@
-using System;
-using System.Diagnostics;
 using CoreGraphics;
 using Foundation;
-using UIKit;
 
 namespace Xamarin.Forms.Platform.iOS
 {
@@ -20,10 +17,7 @@ namespace Xamarin.Forms.Platform.iOS
                        var measure = VisualElementRenderer.Element.Measure(ConstrainedDimension, 
                                double.PositiveInfinity, MeasureFlags.IncludeMargins);
 
-                       var height = VisualElementRenderer.Element.Height > 0 
-                               ? VisualElementRenderer.Element.Height : measure.Request.Height;
-
-                       return new CGSize(ConstrainedDimension, height);
+                       return new CGSize(ConstrainedDimension, measure.Request.Height);
                }
 
                public override void ConstrainTo(CGSize constraint)