Skip to content
4 changes: 4 additions & 0 deletions src/Controls/src/Core/Editor/Editor.Mapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ public partial class Editor
EditorHandler.Mapper.AppendToMapping(nameof(VisualElement.IsVisible), InputView.MapIsVisible);
#endif

#if IOS || MACCATALYST
EditorHandler.Mapper.AppendToMapping<Editor, IEditorHandler>(nameof(AutoSize), MapAutoSize);
#endif

#if ANDROID
EditorHandler.CommandMapper.PrependToMapping(nameof(IEditor.Focus), InputView.MapFocus);
#endif
Expand Down
9 changes: 9 additions & 0 deletions src/Controls/src/Core/Editor/Editor.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ namespace Microsoft.Maui.Controls
{
public partial class Editor
{
// TODO: Make this public in .NET 11
internal static void MapAutoSize(IEditorHandler handler, Editor editor)
Comment thread
Vignesh-SF3580 marked this conversation as resolved.
{
if (handler.PlatformView is Microsoft.Maui.Platform.MauiTextView textView)
{
textView.AllowAutoGrowth = editor.AutoSize == EditorAutoSizeOption.TextChanges;
}
}

public static void MapText(EditorHandler handler, Editor editor) =>
MapText((IEditorHandler)handler, editor);

Expand Down
50 changes: 50 additions & 0 deletions src/Controls/tests/DeviceTests/Elements/Editor/EditorTests.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,56 @@ Task<bool> GetPlatformIsVisible(EditorHandler editorHandler)
});
}

[Fact]
[Description("Editor with AutoSize=TextChanges should continue to grow after a simulated rotation (width constraint change)")]
public async Task AutoSizeTextChangesEditorGrowsAfterRotation()
{
// Regression test for https://github.com/dotnet/maui/issues/35114
// Verifies that AllowAutoGrowth remains true after a width constraint change
// (which is what happens internally during portrait↔landscape rotation).
SetupBuilder();

var editor = new Editor
{
AutoSize = EditorAutoSizeOption.TextChanges,
Text = "Line1\nLine2\nLine3",
WidthRequest = 200,
};

var layout = new VerticalStackLayout
{
WidthRequest = 200,
HorizontalOptions = LayoutOptions.Start,
VerticalOptions = LayoutOptions.Start,
Children = { editor }
};

await AttachAndRun<LayoutHandler>(layout, async (_) =>
{
var frame = editor.Frame;
await WaitForUIUpdate(frame, editor);
var heightBeforeRotation = editor.Height;

// Simulate rotation: portrait → landscape (widen) → portrait (narrow)
frame = editor.Frame;
layout.WidthRequest = 400;
await WaitForUIUpdate(frame, editor);

frame = editor.Frame;
layout.WidthRequest = 200;
await WaitForUIUpdate(frame, editor);

// After simulated rotation, typing more text should still grow the editor
frame = editor.Frame;
editor.Text += "\nLine4\nLine5\nLine6";
await WaitForUIUpdate(frame, editor);

Assert.True(editor.Height > heightBeforeRotation,
$"Editor with AutoSize=TextChanges should still grow after rotation. Before: {heightBeforeRotation}, After: {editor.Height}");
});
}


[Category(TestCategory.Editor)]
public class PlaceholderTests : ControlsHandlerTestBase
{
Expand Down
37 changes: 37 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue35114.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 35114, "Editor can not be scrolled after rotating simulator", PlatformAffected.iOS)]
public class Issue35114 : ContentPage
{
public Issue35114()
{
Title = "Issue 35114";

var slider = new Slider
{
AutomationId = "Slider",
Maximum = 300,
Minimum = 0
};

var editor = new Editor
{
AutomationId = "TestEditor",
Text = "testing"
};

editor.BindingContext = slider;
editor.SetBinding(Editor.CharacterSpacingProperty, new Binding("Value"));

Content = new VerticalStackLayout
{
Children =
{
new Label { Text = "1. Play with the value of the slider below and observe as the space between characters widens." },
new Label { Text = "2. The tests fails if the space between characters does not change." },
Comment thread
Vignesh-SF3580 marked this conversation as resolved.
Outdated
slider,
editor
}
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#if IOS || ANDROID // The SetOrientation method is only supported on mobile platforms.
Comment thread
Vignesh-SF3580 marked this conversation as resolved.
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue35114 : _IssuesUITest
{
public Issue35114(TestDevice device) : base(device)
{
}

public override string Issue => "Editor can not be scrolled after rotating simulator";

[Test]
[Category(UITestCategories.Editor)]
public void EditorCanBeScrolledAfterRotation()
{
// Step 1: Wait for slider and drag it to max
var sliderRect = App.WaitForElement("Slider").GetRect();
App.DragCoordinates(
sliderRect.X + 5,
sliderRect.Y + sliderRect.Height / 2,
sliderRect.X + sliderRect.Width - 5,
sliderRect.Y + sliderRect.Height / 2);
App.WaitForElement("TestEditor");

// Step 2: Get editor height before rotation
var editorRectBefore = App.WaitForElement("TestEditor").GetRect();
var heightBefore = editorRectBefore.Height;

// Step 3: Rotate to landscape
App.SetOrientationLandscape();
App.WaitForElement("TestEditor");

// Step 4: Rotate back to portrait
App.SetOrientationPortrait();
// Allow time for layout to settle after rotation
Task.Delay(2000).Wait();
Comment thread
Vignesh-SF3580 marked this conversation as resolved.
App.WaitForElement("TestEditor");

// Step 5: Get editor height after rotation — should NOT grow
var editorRectAfter = App.WaitForElement("TestEditor").GetRect();
var heightAfter = editorRectAfter.Height;

Assert.That(heightAfter, Is.EqualTo(heightBefore).Within(1),
$"Editor height should not grow after rotation. Before: {heightBefore}, After: {heightAfter}");
Comment thread
Vignesh-SF3580 marked this conversation as resolved.
}
}
#endif
31 changes: 29 additions & 2 deletions src/Core/src/Handlers/Editor/EditorHandler.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,38 @@ public override Size GetDesiredSize(double widthConstraint, double heightConstra

if (double.IsInfinity(heightConstraint))
{
heightConstraint = sizeThatFits.Height;
var currentHeight = (double)PlatformView.Bounds.Height;
Comment thread
Vignesh-SF3580 marked this conversation as resolved.

// NOTE: Bounds and ContentSize reflect the pre-rotation frame at this point.
// The check is intentionally based on the previous layout state to detect
// scrollable Editors that should not grow after cache invalidation (#35114).
// When content overflows the frame and auto-growth is off, cap the height
// to the current frame height to preserve scrollability after rotation (#35114).
// Skip when AllowAutoGrowth is set (AutoSize=TextChanges) — Editor should grow freely.
if (!PlatformView.AllowAutoGrowth
&& currentHeight > 0
&& PlatformView.ContentSize.Height > currentHeight)
{
heightConstraint = currentHeight;
}
else
{
heightConstraint = sizeThatFits.Height;
}
}
}

return base.GetDesiredSize(widthConstraint, heightConstraint);
var result = base.GetDesiredSize(widthConstraint, heightConstraint);

// Cap applies even for finite constraints: UITextView.SizeThatFits (UIScrollView subclass)
// ignores the height argument and always returns full content height.
// Capping here ensures GetDesiredSize honours the caller's constraint.
if (result.Height > heightConstraint)
{
return new Size(result.Width, heightConstraint);
}

return result;
}

public static void MapText(IEditorHandler handler, IEditor editor)
Expand Down
7 changes: 7 additions & 0 deletions src/Core/src/Platform/iOS/MauiTextView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ public UIColor? PlaceholderTextColor

public TextAlignment VerticalTextAlignment { get; set; }

/// <summary>
/// When true, the Editor is allowed to grow beyond its current frame to fit content
/// (AutoSize=TextChanges mode). When false, GetDesiredSize may cap the height to
/// preserve scrollability.
/// </summary>
internal bool AllowAutoGrowth { get; set; }

public override string? Text
{
get => base.Text;
Expand Down
Loading