diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue12193.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue12193.cs new file mode 100644 index 00000000000..8930b5d0808 --- /dev/null +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue12193.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Xamarin.Forms.CustomAttributes; +using Xamarin.Forms.Internals; + +#if UITEST +using Xamarin.Forms.Core.UITests; +using Xamarin.UITest; +using NUnit.Framework; +#endif + +namespace Xamarin.Forms.Controls.Issues +{ +#if UITEST + [Category(UITestCategories.CarouselView)] +#endif + [Preserve(AllMembers = true)] + [Issue(IssueTracker.Github, 12193, "[Bug] CarouselView content disappears after 2 rotations if TextType=Html is used", + PlatformAffected.iOS)] + public class Issue12193 : TestContentPage + { + public const string HTML = "HTML"; + + protected override void Init() + { +#if APP + Title = "CarouselView HTML Label"; + + var instructions = new Label { Text = $"Rotate the device, then rotate it back 3 times. If the label \"{HTML}\" disappears, this test has failed." }; + + var source = new List(); + for (int n = 0; n < 10; n++) + { + source.Add($"Item: {n}"); + } + + var template = new DataTemplate(() => + { + var label = new Label + { + TextType = TextType.Html, + Text = $"

{HTML}

", + AutomationId = HTML + }; + + return label; + }); + + var cv = new CarouselView() + { + ItemsSource = source, + ItemTemplate = template, + Loop = false + }; + + var layout = new StackLayout(); + + layout.Children.Add(instructions); + layout.Children.Add(cv); + + Content = layout; +#endif + } + + +#if UITEST + [Test] + public async Task RotatingCarouselViewHTMLShouldNotDisappear() + { + int delay = 3000; + + RunningApp.SetOrientationPortrait(); + await Task.Delay(delay); + + RunningApp.SetOrientationLandscape(); + await Task.Delay(delay); + + RunningApp.SetOrientationPortrait(); + await Task.Delay(delay); + + RunningApp.SetOrientationLandscape(); + await Task.Delay(delay); + + RunningApp.SetOrientationPortrait(); + await Task.Delay(delay); + + RunningApp.WaitForElement(HTML); + } +#endif + } +} diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems index 4d8ddd6434c..d033868c2a4 100644 --- a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems +++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems @@ -13,6 +13,7 @@ Issue10897.xaml + diff --git a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselTemplatedCell.cs b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselTemplatedCell.cs index 0593cb9900a..234933c762b 100644 --- a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselTemplatedCell.cs +++ b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselTemplatedCell.cs @@ -7,26 +7,28 @@ namespace Xamarin.Forms.Platform.iOS public class CarouselTemplatedCell : TemplatedCell { public static NSString ReuseId = new NSString("Xamarin.Forms.Platform.iOS.CarouselTemplatedCell"); + CGSize _constraint; [Export("initWithFrame:")] [Internals.Preserve(Conditional = true)] protected CarouselTemplatedCell(CGRect frame) : base(frame) - { } + { + } public override void ConstrainTo(nfloat constant) { - } - CGSize _constrain; + public override void ConstrainTo(CGSize constraint) { - _constrain = constraint; - Layout(constraint); + ClearConstraints(); + + _constraint = constraint; } public override CGSize Measure() { - return new CGSize(_constrain.Width,_constrain.Height); + return new CGSize(_constraint.Width, _constraint.Height); } protected override (bool, Size) NeedsContentSizeUpdate(Size currentSize) diff --git a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewController.cs b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewController.cs index 2cc3f7ce3dc..5bf9a73a3d4 100644 --- a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewController.cs +++ b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewController.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.Linq; using System.Threading.Tasks; using CoreGraphics; using Foundation; @@ -15,7 +16,6 @@ public class CarouselViewController : ItemsViewController CarouselViewLoopManager _carouselViewLoopManager; bool _initialPositionSet; - bool _viewInitialized; bool _updatingScrollOffset; List _oldViews; int _gotoPosition = -1; @@ -64,23 +64,12 @@ public override void ViewDidLoad() public override void ViewWillLayoutSubviews() { base.ViewWillLayoutSubviews(); - if (!_viewInitialized) - { - _viewInitialized = true; - _size = CollectionView.Bounds.Size; - } - UpdateVisualStates(); } public override void ViewDidLayoutSubviews() { base.ViewDidLayoutSubviews(); - if (CollectionView.Bounds.Size != _size) - { - _size = CollectionView.Bounds.Size; - BoundsSizeChanged(); - } if (Carousel?.Loop == true && _carouselViewLoopManager != null) { @@ -88,7 +77,20 @@ public override void ViewDidLayoutSubviews() _carouselViewLoopManager.CenterIfNeeded(CollectionView, IsHorizontal); _updatingScrollOffset = false; } + UpdateInitialPosition(); + + if (CollectionView.Bounds.Size != _size) + { + _size = CollectionView.Bounds.Size; + BoundsSizeChanged(); + } + } + + void BoundsSizeChanged() + { + //if the size changed center the item + Carousel.ScrollTo(Carousel.Position, position: Xamarin.Forms.ScrollToPosition.Center, animate: false); } public override void DraggingStarted(UIScrollView scrollView) @@ -105,7 +107,7 @@ public override void UpdateItemsSource() { UnsubscribeCollectionItemsSourceChanged(ItemsSource); base.UpdateItemsSource(); - //we don't need to Subscribe becasse base calls CreateItemsViewSource + //we don't need to Subscribe because base calls CreateItemsViewSource _carouselViewLoopManager?.SetItemsSource(LoopItemsSource); _initialPositionSet = false; UpdateInitialPosition(); @@ -137,18 +139,6 @@ protected override IItemsViewSource CreateItemsViewSource() return itemsSource; } - protected void BoundsSizeChanged() - { - //we might be rotating our phone - ItemsViewLayout.ConstrainTo(CollectionView.Bounds.Size); - - //We call ReloadData so our VisibleCells also update their size - CollectionView.ReloadData(); - - //if the size changed center the item - Carousel.ScrollTo(Carousel.Position, position: Xamarin.Forms.ScrollToPosition.Center, animate: false); - } - internal void TearDown() { Carousel.PropertyChanged -= CarouselViewPropertyChanged; diff --git a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewLayout.cs b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewLayout.cs index 764aa1a328e..afe33da5e8d 100644 --- a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewLayout.cs +++ b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewLayout.cs @@ -16,11 +16,6 @@ public CarouselViewLayout(ItemsLayout itemsLayout, CarouselView carouselView) : _itemsLayout = itemsLayout; } - public override bool ShouldInvalidateLayoutForBoundsChange(CGRect newBounds) - { - return false; - } - public override void ConstrainTo(CGSize size) { //TODO: Should we scale the items @@ -37,12 +32,6 @@ public override void ConstrainTo(CGSize size) } } - internal override void UpdateConstraints(CGSize size) - { - ConstrainTo(size); - UpdateCellConstraints(); - } - public override nfloat GetMinimumInteritemSpacingForSection(UICollectionView collectionView, UICollectionViewLayout layout, nint section) { if (_itemsLayout is LinearItemsLayout linearItemsLayout) diff --git a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewRenderer.cs b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewRenderer.cs index 5f3a5444521..6cb59d63423 100644 --- a/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewRenderer.cs +++ b/Xamarin.Forms.Platform.iOS/CollectionView/CarouselViewRenderer.cs @@ -24,6 +24,12 @@ protected override void ScrollToRequested(object sender, ScrollToRequestEventArg if (Carousel?.Loop == true) { var goToIndexPath = Controller.GetScrollToIndexPath(args.Index); + + if (!IsIndexPathValid(goToIndexPath)) + { + return; + } + Controller.CollectionView.ScrollToItem(goToIndexPath, args.ScrollToPosition.ToCollectionViewScrollPosition(_layout.ScrollDirection), args.IsAnimated); diff --git a/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewController.cs b/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewController.cs index 8409d41f87d..3716f4f32c7 100644 --- a/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewController.cs +++ b/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewController.cs @@ -393,6 +393,5 @@ void UpdateEmptyViewVisibility(bool isEmpty) _emptyViewDisplayed = false; } } - } } diff --git a/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewLayout.cs b/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewLayout.cs index 581047f1fb6..9085c91aa39 100644 --- a/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewLayout.cs +++ b/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewLayout.cs @@ -527,15 +527,9 @@ public override bool ShouldInvalidateLayoutForBoundsChange(CGRect newBounds) return base.ShouldInvalidateLayoutForBoundsChange(newBounds); } - return true; - } + UpdateConstraints(CollectionView.AdjustedContentInset.InsetRect(newBounds).Size); - public override void InvalidateLayout() - { - if (CollectionView != null && CollectionView.Frame != null) { - UpdateConstraints(CollectionView.Frame.Size); - } - base.InvalidateLayout(); + return true; } } } diff --git a/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewRenderer.cs b/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewRenderer.cs index acea257b9ce..fa17941a259 100644 --- a/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewRenderer.cs +++ b/Xamarin.Forms.Platform.iOS/CollectionView/ItemsViewRenderer.cs @@ -233,7 +233,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - bool IsIndexPathValid(NSIndexPath indexPath) + protected bool IsIndexPathValid(NSIndexPath indexPath) { if (indexPath.Item < 0 || indexPath.Section < 0) {