Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

How to identify largest font size for a label, to fit in a specified amount of space

JohnHardmanJohnHardman GBUniversity admin

I need to fit a Label in the space available to the left of other controls, after those other controls have been positioned. On a phone in portrait mode, there isn't much space available, so I need to adjust the font size of the Label until the Label fits without being clipped. Can anybody tell me please how to identify at run-time the largest font that I can use for a Label without the Label being clipped?

Many thanks,

John H.

Best Answer

Answers

  • JohnHardmanJohnHardman GBUniversity admin
    edited November 2015

    Found a hacky, iterative solution. Any elegant, ideally one pass, solutions?

  • powerdudepowerdude USMember ✭✭

    @JohnHardman how about including the answer?

  • JohnHardmanJohnHardman GBUniversity admin

    @powerdude - as the day has gone on, I've rewritten and rewritten. What started as generic but horrible code has now been simplified to something completely non-generic that works for the scenario I have.

    Both versions involved overriding OnSizeAllocated and implementing a SizeChanged handler.

    The generic version started off by populating a Label with a Large font, calculating the screen coordinate of the end of the Label, and comparing that with the start coordinate of the next control. If the Label overlapped with the following control, I changed the font to Medium and repeated, working down through Small and Micro until something fitted. The code was horrible, slow, recursive, and fragile.

    The simplified version, that is adequate for the scenario I have, is as follows. There are still magic numbers in there that I will replace, but the key thing is that in my scenario I can get away with using a mapping between length and font size (done using if/elseif/elseif/else below). It's not a great solution, but it works, and copes with changing orientation, resizing the font and repositioning as required. It's not thoroughly tested, so may still have issues, so buyer beware :-)

        private void OnPageSizeChanged(object sender, EventArgs args)
        {
            UpdateLayout(this.Width, this.Height);
        }
    
        protected override void OnSizeAllocated(double width, double height)
        {
            base.OnSizeAllocated(width, height);
    
            UpdateLayout(width, height);
        }
    
        private void UpdateLayout(double width, double height)
        {
            int stackLayoutWidth = (int) ((width / 2) - 60 - 30 - 5 - _hslTime.X); // we know the button dimensions
    
            if ((int) _hslTime.WidthRequest != stackLayoutWidth)
            {
                _hslTime.HorizontalOptions = LayoutOptions.Start;
                _hslTime.WidthRequest = stackLayoutWidth;
            }
    
            double fontSize;
    
            System.Diagnostics.Debug.WriteLine(String.Format("stackLayoutWidth = {0}", stackLayoutWidth));
    
            if (stackLayoutWidth > 100)
                fontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label));
            else if (stackLayoutWidth > 75)
                fontSize = Device.GetNamedSize(NamedSize.Medium, typeof(Label));
            else if (stackLayoutWidth > 60)
                fontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label));
            else 
                fontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label));
    
            if ((int) _lblTime.FontSize != (int) fontSize)
            {
                _lblTime.HorizontalOptions = LayoutOptions.Center;
                _lblTime.FontSize = (int)fontSize;
                _lblTime.WidthRequest = (int) (stackLayoutWidth - _hslTime.Padding.HorizontalThickness - 7);
            }
        }
    
  • JohnHardmanJohnHardman GBUniversity admin

    Here's the rest...

        private StackLayout _hslTime = null;
        private Label _lblTime = null;
    
                _lblTime = new Label
                {
                    BackgroundColor = Color.White,
                    Text = "00:49:21",                               // just for testing
                    TextColor = Color.Black,
                    XAlign = TextAlignment.Center,
                    YAlign = TextAlignment.Center,
                    HorizontalOptions = LayoutOptions.Center,
                    VerticalOptions = LayoutOptions.Center,
                    LineBreakMode = Xamarin.Forms.LineBreakMode.NoWrap,
                    FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label))
                };
    
                _hslTime = new StackLayout
                {
                    BackgroundColor = Color.White,
                    VerticalOptions = LayoutOptions.Center,
                    HorizontalOptions = LayoutOptions.Start,
                    Orientation = StackOrientation.Horizontal,
                    Padding = new Thickness(5.0, 0, 5.0, 0),
                    Children =
                    {
                        _lblTime
                    }
                };
    
  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @JohnHardman
    You have seen the XLabs solution here?
    https://forums.xamarin.com/discussion/24420/font-size-best-practices
    Is this no solution for your problem...?

  • JohnHardmanJohnHardman GBUniversity admin
    edited November 2015

    @FredyWenger - Hi Fredy. I don't think that provides what is required. What I needed (I've got something working, but it's not generic) is something that given a string and a specific amount of space on screen (in my case, to the left of a group of buttons that are centered in the middle of the screen), works out the largest font size that can be used for that string, without hitting the controls further to the right.

    So, in my case, in the attached picture, I wanted the time period to be in the largest possible font without pushing the buttons off center.

    What is really required are the equivalent of the old Win32 methods, GetTextExtentPoint32 and GetTextMetrics.

  • FredyWengerFredyWenger CHInsider ✭✭✭✭✭

    @JohnHardman:
    It seems as you search for a dynamic version (length of labeltext is dynamic)...?
    If I understand the XLabs solution correct, the font-size should be calculated dynamically depending of the device resolution. So if your label fit's on a line on device A, it also should fit on device B.

  • JohnHardmanJohnHardman GBUniversity admin

    I am now using code based on LabelSizeFontToFit from https://github.com/FormsCommunityToolkit/FormsCommunityToolkit for Android and iOS. Unfortunately, that doesn't have an implementation for UWP, so I am using a non-generic solution for UWP currently. May re-visit this at a later date.

  • JohnHardmanJohnHardman GBUniversity admin

    Thanks @JGoldberger . As long as Width or MaxWidth is specified, that does seem to work. Just need to decide now whether to have my existing custom LabelRenderer (on UWP) switch native control to the ViewBox when required, or whether to use a new view (representing the ViewBox) with its own renderer on UWP. I've done the latter for the moment even though that results in either having extra baggage on Android and iOS, or having different UI hierarchies on UWP to Android and iOS. It seems to be icky whichever option I go for, but at least it works :-)

Sign In or Register to comment.