Open jonmdev opened 1 year ago
Hi @jonmdev. We have added the "s/try-latest-version" label to this issue, which indicates that we'd like you to try and reproduce this issue on the latest available public version. This can happen because we think that this issue was fixed in a version that has just been released, or the information provided by you indicates that you might be working with an older version.
You can install the latest version by installing the latest Visual Studio (Preview) with the .NET MAUI workload installed. If the issue still persists, please let us know with any additional details and ideally a reproduction project provided through a GitHub repository.
This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.
[On MAUI 7.0.86 - iOS]
I've also experienced 1 & 2. In our app we've resorted to using a "go-to-bottom" button that moves your cursor and scrolls to the bottom the editor for you.
With some finagling we were able to get the editor somewhat working in a ScrollView—but it's still pretty broken. After \<some amount> of lines have been entered, you can freely scroll. But before you hit that \<some amount> of lines, you can only initiate a scroll by pulling down first.
I tried .NET 8.0 and it appears some of this has been fixed:
1) It now does scroll down as you type. 2) It now does respond to touch and drag.
However the issues are now:
1) Still has a frame lag as you type new lines (with same project code above). 2) Scrolling by touch and drag is not constrained, so you can slide it up all the way out of view. 3) The new line auto-scrolling is glitchy - if you keep pressing enter it will keep moving the white text field higher and higher until it goes out of view.
Very, very strange.
But I suppose an improvement. At least we have touch/drag response of some kind. 👍
Any further help with fixing this?
To see the problem, open my code in .NET 8, build to iOS then start typing lots of new lines in the field. It will lag on resize, start pushing the text field up high out of view, and if you drag it you will find you can slide it up way out of bounds as well without anything to automatically stop it or bring it back into view after you release.
Thanks.
I posted a simple demo project of the code I posted above here if it is any help to show the issues:
I see this is added to the .NET 8 SR2 milestone and title was changed of post to "Editor causes visible frame lag in resizing parents when it resizes".
I am happy it is being fixed. I hope the other issues of:
Will also be solved.
I just checked and they are still reproduced with my demo project using 8.0.0-rc.2.9373 and it still demonstrates these bugs also.
To clarify, if you open the bug project, click in the touch field for Editor, and start pressing "return" repeatedly in iOS, it will start looking normal like this but eventually the white text field will slide completely off screen:
Once the white field becomes big enough, you can also slide it unconstrained up and down to this degree (even all the way off screen).
I also hope we can have an option to disable being able to pull the text field past its edges at all so the touch/drag function matches better against Android.
Thanks again.
@jonmdev I've had better luck using a StackLayout instead of VerticalStackLayout for most of my layout issues, so you may be interested in that as a workaround. (I know it's "less performant" but I'd personally rather have it actually work in the first place)
I can't share real xaml/code because my repo isn't public, but I can share the layout structure I use for my Editor:
<ContentPage>
<StackLayout Orientation="Vertical">
<header views>
<AbsoluteLayout VerticalOptions="FillAndExpand">
<Editor
AbsoluteLayout.LayoutFlags="All"
AbsoluteLayout.LayoutBounds="0,0,1,1">
<Editor.Behaviors>
<toolkit:UserStoppedTypingBehavior />
</Editor.Behaviors>
</Editor>
<ImageButton
AbsoluteLayout.LayoutFlags="PositionProportional"
AbsoluteLayout.LayoutBounds="1,1"
WidthRequest="44"
HeightRequest="44"
CornerRadius="22"
Margin="10"
Padding="5">
<ImageButton.Shadow>
<Shadow />
</ImageButton.Shadow>
<ImageButton.Source>
<FontImageSource />
</ImageButton.Source>
</ImageButton>
</AbsoluteLayout>
</StackLayout>
</ContentPage>
The AbsoluteLayout is only there so I can float a button over top of the Editor. It may help with the layout issues, but I haven't tested without it, so I don't know. We used a Grid before switching to a StackLayout and left the AbsoluteLayout structure unchanged.
With this structure, and on MAUI 8 RC2, I've had zero issues with the Editor:
I have been able to identify that the resize lagging behavior for surrounding elements is more generalized and not specifically just due to Editor. I have created a separate repro and bug report then for that here that more easily demonstrates the problem:
https://github.com/dotnet/maui/issues/18204
As this is an Editor related bug thread, I will change the title back to reflect the Editor specific bugs as best I understand the current ones. Hopefully that's okay. Again, I hope all these bugs can be fixed as they are all making things quite dysfunctional and not very usable in iOS.
Perhaps all the bugs are actually the same thing? Ie. Maybe the same resize lag/glitch is what is causing the Editor to go off screen when you keep pressing 'return' as well? I am not sure. Maybe if we get that fixed it will fix everything?
@samhouts @Eilon
@jonmdev I've had better luck using a StackLayout instead of VerticalStackLayout for most of my layout issues, so you may be interested in that as a workaround. (I know it's "less performant" but I'd personally rather have it actually work in the first place)
I can't share real xaml/code because my repo isn't public, but I can share the layout structure I use for my Editor:
<ContentPage> <StackLayout Orientation="Vertical"> <header views> <AbsoluteLayout VerticalOptions="FillAndExpand"> <Editor AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1"> <Editor.Behaviors> <toolkit:UserStoppedTypingBehavior /> </Editor.Behaviors> </Editor> <ImageButton AbsoluteLayout.LayoutFlags="PositionProportional" AbsoluteLayout.LayoutBounds="1,1" WidthRequest="44" HeightRequest="44" CornerRadius="22" Margin="10" Padding="5"> <ImageButton.Shadow> <Shadow /> </ImageButton.Shadow> <ImageButton.Source> <FontImageSource /> </ImageButton.Source> </ImageButton> </AbsoluteLayout> </StackLayout> </ContentPage>
The AbsoluteLayout is only there so I can float a button over top of the Editor. It may help with the layout issues, but I haven't tested without it, so I don't know. We used a Grid before switching to a StackLayout and left the AbsoluteLayout structure unchanged.
With this structure, and on MAUI 8 RC2, I've had zero issues with the Editor:
- Editor correctly auto-scrolls when entering newlines past its bottom boundary
- Editor correctly scrolls to cursor on TextChanged when cursor is outside its viewport
- Editor correctly scrolls to cursor position when cursor position is set outside its viewport
- Scrolling behaves as expected compared to other scrolling views like CollectionView
Thanks @tschbc for recommending the absolutelayout
. I had a similar overlay editor layout and I was previously using a grid and that was causing some silly issues that seemed to autocorrect after the switch. You're the man!
Can confirm the behavior below on .NET 7 and .NET 8. Also appears to be completely separate from AutoKeyboardScrollManager scroll logic (for myself and others).
.NET 7 | .NET 8 |
---|---|
Can you test with the latest nightly build? https://github.com/dotnet/maui/wiki/Nightly-Builds
@PureWeen i treid the nightly build and my project is not compiling. now it seems to be that platformview in handlers are object type and not the native type. and color.toPltform() is also broken. so cant test it.
Verified this issue with Visual Studio Enterprise 17.10 Preview 1.0, can repro on iOS platform with sample project. DemoProj.zip
Go ahead and drop this handler in (only target your iOS platform), and it should hopefully make things more bearable:
/// <summary>
/// A handler for the <see cref="Editor"/> control.
/// </summary>
internal class EditorHandler : Microsoft.Maui.Handlers.EditorHandler
{
/// <summary>
/// Gets the desired size of the Editor.
/// </summary>
/// <param name="widthConstraint"></param>
/// <param name="heightConstraint"></param>
/// <returns></returns>
public override Size GetDesiredSize( double widthConstraint, double heightConstraint )
{
//
// Port of the default GetDesiredSizeFromHandler method (since internal):
// https://github.com/dotnet/maui/blob/c7d1a4ec8d857aba674362d2777d98855f0ca67a/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs#L66
//
if ( PlatformView == null || VirtualView == null )
{
return new Size( widthConstraint, heightConstraint );
}
// The measurements ran in SizeThatFits percolate down to child views
// So if MaximumWidth/Height are not taken into account for constraints, the children may have wrong dimensions
widthConstraint = Math.Min( widthConstraint, VirtualView.MaximumWidth );
heightConstraint = Math.Min( heightConstraint, VirtualView.MaximumHeight );
CGSize sizeThatFits = PlatformView.SizeThatFits( new CGSize( ( float ) widthConstraint, ( float ) heightConstraint ) );
var size = new Size(
sizeThatFits.Width == float.PositiveInfinity ? double.PositiveInfinity : sizeThatFits.Width,
sizeThatFits.Height == float.PositiveInfinity ? double.PositiveInfinity : sizeThatFits.Height );
if ( double.IsInfinity( size.Width ) || double.IsInfinity( size.Height ) )
{
PlatformView.SizeToFit();
size = new Size( PlatformView.Frame.Width, PlatformView.Frame.Height );
}
var finalWidth = ResolveConstraints( size.Width, VirtualView.Width, VirtualView.MinimumWidth, VirtualView.MaximumWidth );
var finalHeight = ResolveConstraints( size.Height, VirtualView.Height, VirtualView.MinimumHeight, VirtualView.MaximumHeight );
return new Size( finalWidth, finalHeight );
}
/// <summary>
/// Port of the internal ResolveConstraints method.
/// </summary>
/// <param name="measured"></param>
/// <param name="exact"></param>
/// <param name="min"></param>
/// <param name="max"></param>
/// <returns></returns>
internal static double ResolveConstraints( double measured, double exact, double min, double max )
{
var resolved = measured;
min = Microsoft.Maui.Primitives.Dimension.ResolveMinimum( min );
if ( Microsoft.Maui.Primitives.Dimension.IsExplicitSet( exact ) )
{
// If an exact value has been specified, try to use that
resolved = exact;
}
if ( resolved > max )
{
// Apply the max value constraint (if any)
// If the exact value is in conflict with the max value, the max value should win
resolved = max;
}
if ( resolved < min )
{
// Apply the min value constraint (if any)
// If the exact or max value is in conflict with the min value, the min value should win
resolved = min;
}
return resolved;
}
}
This is the sus code (from the iOS EditorHandler). The problem only occurs for us when the height constraint is coming in as infinity, which also plays into the earlier messages about how VerticalStackLayout was not playing nicely.
Oddly enough, contrary to what the comment says, when I comment out the sus code no exceptions are thrown and everything seems to play nicely:
public override Size GetDesiredSize( double widthConstraint, double heightConstraint )
{
//if ( double.IsInfinity( widthConstraint ) || double.IsInfinity( heightConstraint ) )
//{
// // If we drop an infinite value into base.GetDesiredSize for the Editor, we'll
// // get an exception; it doesn't know what do to with it. So instead we'll size
// // it to fit its current contents and use those values to replace infinite constraints
// PlatformView.SizeToFit();
// if ( double.IsInfinity( widthConstraint ) )
// {
// widthConstraint = PlatformView.Frame.Size.Width;
// }
// if ( double.IsInfinity( heightConstraint ) )
// {
// heightConstraint = PlatformView.Frame.Size.Height;
// }
//}
var desiredSize = base.GetDesiredSize( widthConstraint, heightConstraint );
Debug.WriteLine( "constraints:" + widthConstraint + "x" + heightConstraint );
Debug.WriteLine( "size: " + desiredSize.Width + "x" + desiredSize.Height );
return desiredSize;
}
The ouput (that I slimmed down) seems to properly respect everything:
[0:] constraints:432x∞
[0:] size: 43.666666666666664x169
[0:] constraints:432x∞
[0:] size: 43.666666666666664x188
[0:] constraints:432x∞
[0:] size: 43.666666666666664x200
<Editor
MaximumHeightRequest="200"
MinimumHeightRequest="100"
BackgroundColor="Red"
AutoSize="TextChanges" />
Before (code not commented out):
After (code commented out):
Any updates?
Please take into account while working on this issue that there can be no AccessoryView
or AccessoryView
with one or two bars - one with Done button and the oder with Spellcheck (IsSpellCheckEnabled
) and/or Suggestions (IsTextPredictionEnabled
) when KeyboardFlags
for the custom Keyboard
type are specified.
Any update on this issue, I currently have this problem using version 8.0.90.
Thank you @jfversluis for encouraging me to check again on the workarounds posted here. I think when I last looked at them, we were still dealing with the major border and other iOS drawing problems and animations that @albyrock87 fixed since. So it was hard to evaluate it properly back then.
But I tried overnight re-implementing @bradencohen 's code here:
https://github.com/dotnet/maui/issues/17757#issuecomment-2025779894
This does seem to work now with no obvious issues I can see.
For anyone unfamiliar with implementing a custom handler, copy and paste this into a document like CustomEditorHandler.cs within your usual project namespace:
namespace PROJECT.NAMESPACE {
#if IOS
using CoreGraphics;
/// <summary>
/// A handler for the <see cref="Editor"/> control. //https://github.com/dotnet/maui/issues/17757
/// </summary>
internal class CustomIOSEditorHandler : Microsoft.Maui.Handlers.EditorHandler {
/// <summary>
/// Gets the desired size of the Editor.
/// </summary>
/// <param name="widthConstraint"></param>
/// <param name="heightConstraint"></param>
/// <returns></returns>
public override Size GetDesiredSize(double widthConstraint, double heightConstraint) {
//
// Port of the default GetDesiredSizeFromHandler method (since internal):
// https://github.com/dotnet/maui/blob/c7d1a4ec8d857aba674362d2777d98855f0ca67a/src/Core/src/Handlers/ViewHandlerExtensions.iOS.cs#L66
//
if (PlatformView == null || VirtualView == null) {
return new Size(widthConstraint, heightConstraint);
}
// The measurements ran in SizeThatFits percolate down to child views
// So if MaximumWidth/Height are not taken into account for constraints, the children may have wrong dimensions
widthConstraint = Math.Min(widthConstraint, VirtualView.MaximumWidth);
heightConstraint = Math.Min(heightConstraint, VirtualView.MaximumHeight);
CGSize sizeThatFits = PlatformView.SizeThatFits(new CGSize((float)widthConstraint, (float)heightConstraint));
var size = new Size(
sizeThatFits.Width == float.PositiveInfinity ? double.PositiveInfinity : sizeThatFits.Width,
sizeThatFits.Height == float.PositiveInfinity ? double.PositiveInfinity : sizeThatFits.Height);
if (double.IsInfinity(size.Width) || double.IsInfinity(size.Height)) {
PlatformView.SizeToFit();
size = new Size(PlatformView.Frame.Width, PlatformView.Frame.Height);
}
var finalWidth = ResolveConstraints(size.Width, VirtualView.Width, VirtualView.MinimumWidth, VirtualView.MaximumWidth);
var finalHeight = ResolveConstraints(size.Height, VirtualView.Height, VirtualView.MinimumHeight, VirtualView.MaximumHeight);
return new Size(finalWidth, finalHeight);
}
/// <summary>
/// Port of the internal ResolveConstraints method.
/// </summary>
/// <param name="measured"></param>
/// <param name="exact"></param>
/// <param name="min"></param>
/// <param name="max"></param>
/// <returns></returns>
internal static double ResolveConstraints(double measured, double exact, double min, double max) {
var resolved = measured;
min = Microsoft.Maui.Primitives.Dimension.ResolveMinimum(min);
if (Microsoft.Maui.Primitives.Dimension.IsExplicitSet(exact)) {
// If an exact value has been specified, try to use that
resolved = exact;
}
if (resolved > max) {
// Apply the max value constraint (if any)
// If the exact value is in conflict with the max value, the max value should win
resolved = max;
}
if (resolved < min) {
// Apply the min value constraint (if any)
// If the exact or max value is in conflict with the min value, the min value should win
resolved = min;
}
return resolved;
}
}
#endif
}
And then in MauiProgram.cs add something like:
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts => {
//whatever
})
.ConfigureMauiHandlers((handlers) => {
#if IOS
handlers.AddHandler<Microsoft.Maui.Controls.Editor, CustomIOSEditorHandler>(); //FIX SCROLLING ISSUE https://github.com/dotnet/maui/issues/17757
#endif
});
I have only briefly tested it so it may need more rigorous examination. But in combination with all the prior Border and iOS animation fixes it appears we may have a working Editor now after all. Thanks again to all. 🙂👍
It seems Maui is getting close to production ready.
Description
A working Editor is essential for text input in a cross platform and mobile environment. Editor can be used as a field for users to type into such as in this forum box I am typing into now or a WhatsApp or SMS style text entry field.
A typical such Editor function is described by:
In Windows and Android, Editor functions properly with such a setting. However in iOS it is quite broken, exhibiting at least three problems:
Editor in iOS will not scroll down automatically as you add new lines once it is at its max size (in Windows and Android, as you add more lines, the Editor automatically scrolls down to show them).
Editor in iOS does not respond to touch and drag to scroll manually (in Android, you can touch and drag to scroll and Windows use the mouse scroll wheel to scroll).
Parent element of Editor in iOS resizes with a one frame lag behind the Editor as the Editor expands, so as you add new lines and Editor resizes, Editor will momentarily overlap its parent object each time (in Android and Windows the resizing of parents are instant).
Android and Windows exhibit all the obvious desirable behaviors.
That is: (i) A text field must automatically scroll down to always show the current line of text being written to, (ii) touching and dragging is the only way to manually scroll a text field in mobile and must work as expected, and (iii) resizing of the Editor should not introduce visual glitches and resize lags.
These problems only exist in iOS. Behavior of Editor in Android and Windows appears perfect.
Steps to Reproduce
Create a new blank MAUI project in Visual Studio 2022 using .NET 7.0 by File > New.
Copy and paste the following to replace the default class in App.xaml.cs:
Run the project in Windows and Android and you will observe the following CORRECT BEHAVIOR:
Expected behavior again is that Editor in iOS should show the same results as Windows and Android with: (i) automatic downward scrolling on new lines, (ii) manual scrolling on touch and drag (and scroll wheel if available from simulator), and (iii) instant resizing of its parents upon changing size.
Link to public reproduction project repository
https://github.com/jonmdev/Editor-iOS-Bug
Version with bug
7.0.92/8.0
Is this a regression from previous behavior?
Not sure, did not test other versions
Last version that worked well
Unknown/Other
Affected platforms
iOS
Affected platform versions
iOS 16.7 on iPhone XR with Debug build (Hot Restart)
Did you find any workaround?
None.
Relevant log output
No response