Skip to content

Commit

Permalink
Simplify iOS ScrollView (dotnet#26763)
Browse files Browse the repository at this point in the history
  • Loading branch information
albyrock87 authored Dec 27, 2024
1 parent 7a48123 commit bc24a00
Show file tree
Hide file tree
Showing 19 changed files with 403 additions and 317 deletions.
44 changes: 17 additions & 27 deletions src/Controls/src/Core/Handlers/Items2/ItemsViewHandler2.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using CoreGraphics;
using Foundation;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Handlers;
Expand Down Expand Up @@ -67,7 +68,11 @@ private protected override UIView OnCreatePlatformView()

protected abstract ItemsViewController2<TItemsView> CreateController(TItemsView newElement, UICollectionViewLayout layout);

protected override UIView CreatePlatformView() => Controller?.View;
protected override UIView CreatePlatformView()
{
var controllerView = Controller?.View ?? throw new InvalidOperationException("ItemsViewController2's view should not be null at this point.");
return controllerView;
}

public static void MapItemsSource(ItemsViewHandler2<TItemsView> handler, ItemsView itemsView)
{
Expand Down Expand Up @@ -175,35 +180,20 @@ protected bool IsIndexPathValid(NSIndexPath indexPath)
return true;
}

// public override Size GetDesiredSize(double widthConstraint, double heightConstraint)
// {
// var size = base.GetDesiredSize(widthConstraint, heightConstraint);

// var potentialContentSize = Controller.GetSize();


// System.Diagnostics.Debug.WriteLine($"potentialContentSize: {potentialContentSize}");
// // If contentSize comes back null, it means none of the content has been realized yet;
// // we need to return the expansive size the collection view wants by default to get
// // it to start measuring its content
// if (potentialContentSize == null)
// {
// return size;
// }

// var contentSize = potentialContentSize.Value;

// // If contentSize does have a value, our target size is the smaller of it and the constraints
public override Size GetDesiredSize(double widthConstraint, double heightConstraint)
{
var contentSize = Controller.GetSize();

// size.Width = contentSize.Width <= widthConstraint ? contentSize.Width : widthConstraint;
// size.Height = contentSize.Height <= heightConstraint ? contentSize.Height : heightConstraint;
// Our target size is the smaller of it and the constraints
var width = contentSize.Width <= widthConstraint ? contentSize.Width : widthConstraint;
var height = contentSize.Height <= heightConstraint ? contentSize.Height : heightConstraint;

// var virtualView = this.VirtualView as IView;
IView virtualView = VirtualView;

// size.Width = ViewHandlerExtensions.ResolveConstraints(size.Width, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
// size.Height = ViewHandlerExtensions.ResolveConstraints(size.Height, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);
width = ViewHandlerExtensions.ResolveConstraints(width, virtualView.Width, virtualView.MinimumWidth, virtualView.MaximumWidth);
height = ViewHandlerExtensions.ResolveConstraints(height, virtualView.Height, virtualView.MinimumHeight, virtualView.MaximumHeight);

// return size;
// }
return new Size(width, height);
}
}
}
59 changes: 59 additions & 0 deletions src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewController2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public abstract class ItemsViewController2<TItemsView> : UICollectionViewControl
UIView _emptyUIView;
VisualElement _emptyViewFormsElement;
List<string> _cellReuseIds = new List<string>();
CGSize _previousContentSize;

[UnconditionalSuppressMessage("Memory", "MEM0002", Justification = "Proven safe in test: MemoryTests.HandlerDoesNotLeak")]
protected UICollectionViewDelegateFlowLayout Delegator { get; set; }
Expand Down Expand Up @@ -190,6 +191,7 @@ public override void ViewWillLayoutSubviews()
{
base.ViewWillLayoutSubviews();
LayoutEmptyView();
InvalidateMeasureIfContentSizeChanged();
}

void Items.MauiCollectionView.ICustomMauiCollectionViewDelegate.MovedToWindow(UIView view)
Expand Down Expand Up @@ -319,6 +321,63 @@ protected virtual CGRect DetermineEmptyViewFrame()
CollectionView.Frame.Width, CollectionView.Frame.Height);
}

void InvalidateMeasureIfContentSizeChanged()
{
if (CollectionView?.CollectionViewLayout?.CollectionViewContentSize is not { } contentSize)
{
return;
}

var previousContentSize = _previousContentSize;
_previousContentSize = contentSize;

bool widthChanged = previousContentSize.Width != contentSize.Width;
bool heightChanged = previousContentSize.Height != contentSize.Height;

if (_initialized && (widthChanged || heightChanged))
{
if (CollectionView?.Bounds is not { } bounds)
{
return;
}

var cvWidth = bounds.Width;
var cvHeight = bounds.Height;
bool invalidate = false;

// If both the previous content size and the current content size are larger
// than the UICollectionView size, then we know that we're already maxed out and the
// CollectionView items are scrollable. There's no reason to force an invalidation
// of the CollectionView to expand/contract it.

// If either size is smaller than that, we need to invalidate to ensure that the
// CollectionView is re-measured and set to the correct size.

if (widthChanged && (contentSize.Width <= cvWidth || previousContentSize.Width <= cvWidth))
{
invalidate = true;
}
else if (heightChanged && (contentSize.Height <= cvHeight || previousContentSize.Height <= cvHeight))
{
invalidate = true;
}

if (invalidate)
{
(ItemsView as IView)?.InvalidateMeasure();
}
}
}

internal Size GetSize()
{
if (_emptyViewDisplayed)
{
return _emptyUIView.Frame.Size.ToSize();
}

return CollectionView.CollectionViewLayout.CollectionViewContentSize.ToSize();
}

internal void UpdateView(object view, DataTemplate viewTemplate, ref UIView uiView, ref VisualElement formsElement)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#nullable enable
Microsoft.Maui.Controls.StyleableElement.Style.get -> Microsoft.Maui.Controls.Style?
override Microsoft.Maui.Controls.Handlers.Items2.ItemsViewHandler2<TItemsView>.GetDesiredSize(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
~abstract Microsoft.Maui.Controls.Handlers.Items2.ItemsViewHandler2<TItemsView>.CreateController(TItemsView newElement, UIKit.UICollectionViewLayout layout) -> Microsoft.Maui.Controls.Handlers.Items2.ItemsViewController2<TItemsView>
~abstract Microsoft.Maui.Controls.Handlers.Items2.ItemsViewHandler2<TItemsView>.SelectLayout() -> UIKit.UICollectionViewLayout
*REMOVED*~Microsoft.Maui.Controls.Handlers.Compatibility.ShellScrollViewTracker.ShellScrollViewTracker(Microsoft.Maui.IPlatformViewHandler renderer) -> void
Expand Down Expand Up @@ -63,7 +64,6 @@ Microsoft.Maui.Controls.StyleableElement.Style.get -> Microsoft.Maui.Controls.St
*REMOVED*~Microsoft.Maui.Controls.NavigableElement.StyleClass.set -> void
Microsoft.Maui.Controls.HybridWebView.SetInvokeJavaScriptTarget<T>(T! target) -> void
~Microsoft.Maui.Controls.ResourceDictionary.SetAndCreateSource<T>(System.Uri value) -> void

~Microsoft.Maui.Controls.WebViewProcessTerminatedEventArgs.PlatformArgs.get -> Microsoft.Maui.Controls.PlatformWebViewProcessTerminatedEventArgs
~Microsoft.Maui.Controls.Xaml.RequireServiceAttribute.RequireServiceAttribute(System.Type[] serviceTypes) -> void
~Microsoft.Maui.Controls.Xaml.RequireServiceAttribute.ServiceTypes.get -> System.Type[]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#nullable enable
Microsoft.Maui.Controls.StyleableElement.Style.get -> Microsoft.Maui.Controls.Style?
override Microsoft.Maui.Controls.Handlers.Items2.ItemsViewHandler2<TItemsView>.GetDesiredSize(double widthConstraint, double heightConstraint) -> Microsoft.Maui.Graphics.Size
~abstract Microsoft.Maui.Controls.Handlers.Items2.ItemsViewHandler2<TItemsView>.CreateController(TItemsView newElement, UIKit.UICollectionViewLayout layout) -> Microsoft.Maui.Controls.Handlers.Items2.ItemsViewController2<TItemsView>
~abstract Microsoft.Maui.Controls.Handlers.Items2.ItemsViewHandler2<TItemsView>.SelectLayout() -> UIKit.UICollectionViewLayout
*REMOVED*~Microsoft.Maui.Controls.Handlers.Compatibility.ShellScrollViewTracker.ShellScrollViewTracker(Microsoft.Maui.IPlatformViewHandler renderer) -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,16 @@ void SetupBuilder()
public async Task ControlTemplateUpdates()
{
SetupBuilder();
var child = new Label { Text = "Content 1" };
var contentView = new Microsoft.Maui.Controls.ContentView();
var header = new Label { Text = "Header" };
var footer = new Label { Text = "Footer" };
var presenter = new ContentPresenter();
var grid = new Grid();

var contentViewHandler = await CreateHandlerAsync<ContentViewHandler>(contentView);

await InvokeOnMainThreadAsync(() =>
{
var child = new Label { Text = "Content 1" };
var contentView = new Microsoft.Maui.Controls.ContentView();
var header = new Label { Text = "Header" };
var footer = new Label { Text = "Footer" };
var presenter = new ContentPresenter();
var grid = new Grid();
var contentViewHandler = CreateHandler<ContentViewHandler>(contentView);
contentView.Content = child;
Assert.True(GetChildCount(contentViewHandler) == 1);
Assert.True(GetContentChildCount(contentViewHandler) == 0);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue26629.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.Github, 26629, "ScrollView resizes when content changes", PlatformAffected.All)]
public class Issue26629 : ContentPage
{
public Issue26629()
{
var grid = new Grid
{
Margin = 40,
RowSpacing = 16,
BackgroundColor = Colors.Beige,
RowDefinitions = new RowDefinitionCollection(
new RowDefinition(GridLength.Auto),
new RowDefinition(GridLength.Auto),
new RowDefinition(GridLength.Auto),
new RowDefinition(GridLength.Star)),
ColumnDefinitions = new ColumnDefinitionCollection(new ColumnDefinition(GridLength.Star)),
};
var scrollView = new ScrollView { AutomationId = "TheScrollView" };
var scrollViewVsl = new VerticalStackLayout();
var button = new Button
{
Text = "Add Label",
AutomationId = "AddLabelButton",
};

var sizeLabel = new Label
{
AutomationId = "SizeLabel",
};
sizeLabel.SetBinding(Label.TextProperty, new Binding(nameof(View.Height), source: scrollView));

var scrollOffsetLabel = new Label
{
AutomationId = "ScrollOffsetLabel",
};
scrollOffsetLabel.SetBinding(Label.TextProperty, new Binding(nameof(ScrollView.ScrollY), source: scrollView));

var i = 0;
scrollView.BackgroundColor = Colors.LightBlue;
scrollView.Padding = 16;
scrollView.VerticalOptions = LayoutOptions.Start;
scrollViewVsl.Children.Add(CreateLabel("Label0"));
button.Clicked += (sender, args) =>
{
scrollViewVsl.Children.Add(CreateLabel($"Label{++i}"));
};

scrollView.Content = scrollViewVsl;
grid.Add(button, 0, 0);
grid.Add(sizeLabel, 0, 1);
grid.Add(scrollOffsetLabel, 0, 2);
grid.Add(scrollView, 0, 3);

Content = grid;
}

static Label CreateLabel(string automationId)
{
return new Label
{
Text = "Huge",
FontSize = 100,
BackgroundColor = Colors.SlateBlue,
AutomationId = automationId,
};
}
}
}
64 changes: 34 additions & 30 deletions src/Controls/tests/TestCases.HostApp/Issues/Issues17801.xaml
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue17801">
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image
Source="dotnet_bot.png"
HeightRequest="200"
HorizontalOptions="Center" />
<Label
AutomationId="WaitForStubControl"
Text="Hello, World!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />
<Label
Text="Welcome to .NET Multi-platform App UI"
FontSize="18"
HorizontalOptions="Center" />
<Button
x:Name="CounterBtn"
Text="Click me"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue17801">
<ShellContent Title="Home">
<ContentPage>
<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">
<Image
Source="dotnet_bot.png"
HeightRequest="200"
HorizontalOptions="Center" />
<Label
AutomationId="WaitForStubControl"
Text="Hello, World!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />
<Label
Text="Welcome to .NET Multi-platform App UI"
FontSize="18"
HorizontalOptions="Center" />
<Button
x:Name="CounterBtn"
Text="Click me"
Clicked="OnCounterClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ScrollView>
</ContentPage>
</ShellContent>
</Shell>
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.Github, 17801, "ScrollView always has a scroll bar on iOS", PlatformAffected.iOS)]
public partial class Issue17801 : ContentPage
public partial class Issue17801
{
int _count = 0;

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public void HeaderFooterGridWorks()
VerifyScreenshot();
}

#if TEST_FAILS_ON_IOS
// The screenshot that's currently generated for this test is wrong
// So, we're ignoring this test due to it causing confusion when other changes
// cause this test to fail.
[Test]
[Category(UITestCategories.CollectionView)]
public void HeaderFooterGridHorizontalWorks()
Expand All @@ -89,6 +93,7 @@ public void HeaderFooterGridHorizontalWorks()

VerifyScreenshot();
}
#endif
}
#endif
}
Loading

0 comments on commit bc24a00

Please sign in to comment.