Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resize internal content container when ScrollView content is resized #16385

Merged
merged 2 commits into from
Jul 27, 2023
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,39 @@
using Microsoft.Maui.Controls;

namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.Github, 14257, "VerticalStackLayout inside Scrollview: Button at the bottom not clickable on IOS", PlatformAffected.iOS)]
public class Issue14257 : TestContentPage
{
protected override void Init()
{
var scrollView = new ScrollView();
var layout = new VerticalStackLayout() { Margin = new Microsoft.Maui.Thickness(10, 40) };

var description = new Label { Text = "Tap the Resize button; this will force the Test button off the screen. Then tap the Test button; if a Label with the text \"Success\" appears, the test has passed." };

var resizeButton = new Button() { Text = "Resize", AutomationId = "Resize" };
var layoutContent = new Label() { Text = "Content", HeightRequest = 50 };
var testButton = new Button() { Text = "Test", AutomationId = "Test" };
var resultLabel = new Label() { AutomationId = "Result" };

layout.Add(description);
layout.Add(resizeButton);
layout.Add(layoutContent);
layout.Add(resultLabel);
layout.Add(testButton);

scrollView.Content = layout;
Content = scrollView;

resizeButton.Clicked += (sender, args) => {
// Resize the ScrollView content so the test button will be off the screen
// If the bug is present, this will make the button untappable
layoutContent.HeightRequest = 1000;
};

// Show the Success label if the button is tapped, so we can verify the bug is not present
testButton.Clicked += (sender, args) => { resultLabel.Text = "Success"; };
}
}
}
26 changes: 26 additions & 0 deletions src/Controls/tests/UITests/Tests/Issues/Issue14257.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.Maui.Appium;
using NUnit.Framework;

namespace Microsoft.Maui.AppiumTests.Issues
{
public class Issue14257 : _IssuesUITest
{
public Issue14257(TestDevice device) : base(device) { }

public override string Issue => "VerticalStackLayout inside Scrollview: Button at the bottom not clickable on IOS";

[Test]
public void ResizeScrollViewAndTapButtonTest()
{
// Tapping the Resize button will change the height of the ScrollView content
App.Tap("Resize");

// Scroll down to the Test button. When the bug is present, the button cannot be tapped.
App.ScrollDownTo("Test");
App.Tap("Test");

// If we can successfully tap the button, the Success label will be displayed
Assert.IsTrue(App.WaitForTextToBePresentInElement("Result", "Success"));
}
}
}
52 changes: 12 additions & 40 deletions src/Core/src/Handlers/ScrollView/ScrollViewHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,36 +188,6 @@ static void InsertContentView(UIScrollView platformScrollView, IScrollView scrol
platformScrollView.AddSubview(contentContainer);
}

static Size MeasureScrollViewContent(double widthConstraint, double heightConstraint, Func<double, double, Size> internalMeasure, UIScrollView platformScrollView, IScrollView scrollView)
{
var presentedContent = scrollView.PresentedContent;
if (presentedContent == null)
{
return Size.Zero;
}

var scrollViewBounds = platformScrollView.Bounds;
var padding = scrollView.Padding;

if (widthConstraint == 0)
{
widthConstraint = scrollViewBounds.Width;
}

if (heightConstraint == 0)
{
heightConstraint = scrollViewBounds.Height;
}

// Account for the ScrollView Padding before measuring the content
widthConstraint = AccountForPadding(widthConstraint, padding.HorizontalThickness);
heightConstraint = AccountForPadding(heightConstraint, padding.VerticalThickness);

var result = internalMeasure.Invoke(widthConstraint, heightConstraint);

return result.AdjustForFill(new Rect(0, 0, widthConstraint, heightConstraint), presentedContent);
}

public override Size GetDesiredSize(double widthConstraint, double heightConstraint)
{
var virtualView = VirtualView;
Expand Down Expand Up @@ -338,6 +308,16 @@ Size ICrossPlatformLayout.CrossPlatformArrange(Rect bounds)
{
var scrollView = VirtualView;
var platformScrollView = PlatformView;

var contentSize = scrollView.CrossPlatformArrange(bounds);

// The UIScrollView's bounds are available, so we can use them to make sure the ContentSize makes sense
// for the ScrollView orientation
var viewportBounds = platformScrollView.Bounds;
var viewportHeight = viewportBounds.Height;
var viewportWidth = viewportBounds.Width;
SetContentSizeForOrientation(platformScrollView, viewportWidth, viewportHeight, scrollView.Orientation, contentSize);

var container = GetContentView(platformScrollView);

if (container?.Superview is UIScrollView uiScrollView)
Expand All @@ -347,23 +327,15 @@ Size ICrossPlatformLayout.CrossPlatformArrange(Rect bounds)
// container. (Everything will look correct if they do, but hit testing won't work properly.)

var scrollViewBounds = uiScrollView.Bounds;
var containerBounds = container.Bounds;
var containerBounds = contentSize;

container.Bounds = new CGRect(0, 0,
Math.Max(containerBounds.Width, scrollViewBounds.Width),
Math.Max(containerBounds.Height, scrollViewBounds.Height));

container.Center = new CGPoint(container.Bounds.GetMidX(), container.Bounds.GetMidY());
}

var contentSize = scrollView.CrossPlatformArrange(bounds);

// The UIScrollView's bounds are available, so we can use them to make sure the ContentSize makes sense
// for the ScrollView orientation
var viewportBounds = platformScrollView.Bounds;
var viewportHeight = viewportBounds.Height;
var viewportWidth = viewportBounds.Width;
SetContentSizeForOrientation(platformScrollView, viewportWidth, viewportHeight, scrollView.Orientation, contentSize);

return contentSize;
}

Expand Down
Loading
Loading