xamarin / Xamarin.Forms

Xamarin.Forms is no longer supported. Migrate your apps to .NET MAUI.
https://aka.ms/xamarin-upgrade
Other
5.62k stars 1.87k forks source link

[Bug] Editor AutoSize (still) doesn't work on iOS #10406

Open jefffhaynes opened 4 years ago

jefffhaynes commented 4 years ago

Description

This is basically an attempt to call attention to #6182, which was closed but appears to still be an issue.

Steps to Reproduce

  1. At the risk of sounding glib, it is rather difficult to not reproduce this bug when using the editor. However, one of the easiest ways to reproduce it is:
    <StackLayout>
        <Editor BackgroundColor="LightBlue" WidthRequest="200" AutoSize="TextChanges"/>
    </StackLayout>
  1. Typing in the editor will result in bizarre layout behavior with additional spaces being added before the text reaches the extents of the control.

  2. Far more problematic is something like this:

    <StackLayout Orientation="Horizontal">
        <Label Text="Some text"/>
        <Editor BackgroundColor="LightBlue" AutoSize="TextChanges"/>
    </StackLayout>
  1. Typing in the editor results in the text disappearing off the edge of the screen. Depending on layout configuration it is possible to create all manner of unexpected behavior. The behavior is not restricted to StackLayout but seems to happen regardless of the specific container.

Expected Behavior

Multi-line expanding entry type behavior

Actual Behavior

Everything else

Basic Information

Screenshots

image

image

davidortinau commented 4 years ago

@samhouts def looks like something that ought to be improved, looking at comments on #6182.

I tested with 4.0 and 4.6 RC.

@jefffhaynes I found that your example in 1 worked very well. No problems here on iOS 13.4.

The example in 3 naturally results in what you show. The layout needs to know a constraint, and in this case none is provided in a horizontal stack layout. This is perhaps something we can improve, but providing some sense of sizing would be idea. Use a FlexLayout instead and provide a Basis and/or use the Shrink/Grow to achieve the behavior you want.

<FlexLayout Direction="Row">
                    <Label Text="Some text" FlexLayout.Shrink="0"/>
                    <Editor BackgroundColor="LightBlue" AutoSize="TextChanges"/>
                </FlexLayout>
Screen Shot 2020-04-21 at 8 59 29 PM
jefffhaynes commented 4 years ago

Ok, I will experiment with the FlexLayout more in my specific use case. However, I'm confused as to how the first case is working for you. I am also simulating an iPhone 11 iOS 13.4 and I'm definitely having issues. FWIW, I'm doing this on the Windows simulator.

It also looks like you're doing this in some sort of navigation container whereas I'm simply nesting everything inside a ContentPage. Maybe try that?

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core" 
             ios:Page.UseSafeArea="true"
             mc:Ignorable="d"
             x:Class="EditorIssues.MainPage">

    <StackLayout>
        <Editor BackgroundColor="LightBlue" WidthRequest="200" AutoSize="TextChanges"/>
    </StackLayout>

</ContentPage>
jefffhaynes commented 4 years ago

I tried using a FlexLayout without much success. It doesn't really behave the way you would expect except for in very contrived scenarios. Maybe you can give me some idea as to what I should be trying.

FWIW this is for a chat-style UI and thus far I've had no luck putting an editor at the bottom of the page and having it auto-size correctly.

As a side note, if the ListView isn't nested in a FlexLayout then it simply takes over the entire screen. I guess this is a side-effect of the ListView having broken behavior inside layouts but all of this adds up to making layout in Xamarin taxing to say the least.

  <FlexLayout Direction="Column">

        <!-- Header -->
        <Label Text="HEADER"
               FontSize="Large"
               BackgroundColor="Aqua"
               HorizontalTextAlignment="Center" />

        <!-- Body -->
        <FlexLayout FlexLayout.Grow="1">

            <ListView/>

        </FlexLayout>

        <!-- Footer -->
        <Editor FlexLayout.Basis="50" BackgroundColor="DarkGray"/>
    </FlexLayout>

image

Edit: this problem exists irrespective of the keyboard being shown or not (e.g. if the layout is resized or padded to accommodate the keyboard on iOS).

jsuarezruiz commented 4 years ago

This issue could be related (or the same) as #10417.

jefffhaynes commented 4 years ago

I think the fundamental issue here is a lack of real distinction between measure and layout. Roughly speaking, I think the layout system comes in and asks the editor how much space it needs to layout via the SizeThatFits override. The editor gives an optimistic (trivial) response and from then on the layout system is like, great, I'm just going to use that. Except what actually happens is the layout changes around the editor, affecting the element but the concept of that original measurement never changes and as far I can tell, there is no opportunity to update it.

I "fixed" this by creating a custom renderer for the editor and simply overriding SizeThatFits but using the element size, rather than the passed (optimistic) size.

public override CGSize SizeThatFits(CGSize size)
{
    // ignore meaningless size
    var fixedWidth = Element.Bounds.Size.Width;
    var insetsWidth = Control.TextContainerInset.Right + Control.TextContainerInset.Left;
    var newSize = Control.SizeThatFits(new CGSize(fixedWidth, nfloat.MaxValue));
    var width = Math.Max((float)newSize.Width, (float)fixedWidth) + insetsWidth;
    var height = Math.Ceiling(newSize.Height);
    return new CGSize(width, height);
}

FWIW similar tricks are used in the LabelRenderer, which makes me think there is some validity to the approach. It would seem to suggest an issue with the layout system as there is no clear break between the control layout and the element layout and it isn't clear where one takes precedence vs. the other.

Tommigun1980 commented 4 years ago

I must be missing something, as

                    <Editor
                        AutoSize="TextChanges"
                        MaxLength="80"/>

inside a StackLayout, on iOS (haven't tested other platforms) seems to completely ignore AutoSize. The editor always displays roughly five lines. The result is exactly the same as without 'AutoSize="TextChanges"'.

Is this just completely broken or am I doing something wrong? Is there any hope at all for this to get fixed? I am using Xamarin.Froms 4.8.0-pre3.

jefffhaynes commented 4 years ago

@Tommigun1980 pretty sure it's completely broken

PureWeen commented 3 years ago

This doesn't seem like a bug with AutoSize. A bug with AutoSize would be a scenario where a given text would layout differently if it was initially set vs typed in

AutoSize appears to be working fine here (unless I've missed something in the discussion)

For example if I take the xaml

    <StackLayout Orientation="Horizontal">
        <Label Text="Some text"/>
        <Editor Text="oaihweifa wieuf awif aiwuiefh aiwehf aiwueh faiwuheif wuehfa iehfiuwahef aiwuif aiwieuf iawuefhlawe" BackgroundColor="LightBlue" AutoSize="TextChanges" />
    </StackLayout>

The Editor will layout the same way if I start it out with a bunch of Text vs type the Text in The above SL produces the same layout on Android/iOS

If you use a Label here then the Label does wrap, so the bug seems more to do with how constraints are interpreted on an Editor

I haven't really seen here how AutoSize="TextChanges" itself is broken

devperson commented 3 years ago

Auto size does not work when text is set via binding, I don't think this requires any repro project JUST BIND EDITOR TO ANY LONG TEXT and you will have a bug with auto sizing

lobbo232 commented 3 years ago

This is still an issue in the latest of 5.0, as is typical of the Xamarin team.

The FlexLayout is not a workaround for all scenarios. When the entry is within a ListView and a FlexLayout is used, the resulting behaviour forces UI elements out of the view when Editor wraps below.

image image

<StackLayout Orientation="Horizontal">
    <Label Text="Address:"/>
    <Editor AutoSize="TextChanges"/>
</StackLayout>
mackayn commented 2 years ago

This is still an issue in the latest of 5.0, as is typical of the Xamarin team.

The FlexLayout is not a workaround for all scenarios. When the entry is within a ListView and a FlexLayout is used, the resulting behaviour forces UI elements out of the view when Editor wraps below.

image image

<StackLayout Orientation="Horizontal">
    <Label Text="Address:"/>
    <Editor AutoSize="TextChanges"/>
</StackLayout>

Yeah, can confirm AutoSize does nothing.

The last reported workaround here helped my scenario https://github.com/xamarin/Xamarin.Forms/issues/9550

Zografska commented 1 year ago

@jefffhaynes Hi Jeff, I managed to fix it by creating my custom renderer and overriding OnElementPropertyChanged like this:

   protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (Control == null)
            {
                return;
            }

            if (e.PropertyName == "Text")
            {
                var contentSize = Control.SizeThatFits(Control.Bounds.Size);
                Control.Frame = new CGRect(5, 0, Control.Frame.Size.Width - 2 * 5, contentSize.Height);
            }
        }

Hope it helps if you still have the problem :)