전체 페이지뷰

2017년 2월 24일 금요일

Chapter 5. Dealing with sizes, part 2

Metrical sizes(미터법 사이즈)


이제 Xamarin.Forms 응용 프로그램의 크기가 대략 미터 및 센티미터 치수와 어떻게 일치하는지 알게 되었으므로, 요소의 크기를 조정하여 다양한 장치에서 거의 동일한 크기가 되도록 만들 수 있습니다. 다음은 약 1cm의 너비와 약 1 인치의 높이로 BoxView를 표시하는 MetricalBoxView 프로그램입니다.

public class MetricalBoxViewPage : ContentPage
{
    public MetricalBoxViewPage()
    {
        Content = new BoxView
        {
            Color = Color.Accent,
            WidthRequest = 64,
            HeightRequest = 160,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center
        };
    }
}
cs

스마트폰 상에서 자로 재어보면, 정확히 원하는 크기는 아니지만 아주 근접한 크기인 것을 확인할 수 있을 겁니다.


이 프로그램은 핸드폰에서 돌아가도록 된 것입니다. 태블릿 상에서 실행하려면 Device.Idiom 속성을 사용하여 iPad 및 Windows 태블릿용의 조금 작은 배율 factor를 설정할 수 있습니다.

Estimated font sizes

LabelButtonFontSize 속성은 diacritical 마크(악센트 같은) 같은 것도 전부 포함한 문자의 대략적인 높이를 지정합니다. 대부분은 이 속성을 Device.GetNamedSize 메서드에서 반환한 값으로 설정하는데  NamedSize 열거형의 멤버인 Default, Micro, Small, Medium, Large를 사용할 수 있습니다.

또는 FontSize 속성을 실제 숫자로도 설정할 수 있지만 거기에는 약간의 문제가 있습니다 (자세한 내용은 곧 다룹니다). 대부분, Xamarin.Forms 전체에서 사용되는 동일한 장치 독립적인 단위로 글꼴 크기를 지정하는데 플랫폼 해상도에 따라 장치 독립적 글꼴 크기를 계산이 가능합니다.

예를 들어, 인쇄물이나 데스코톱 화면에서는 적당하지만 전화기에는 좀 큰 크기인 12 포인트를 사용한다고 가정해 봅시다. 1인치에는 72 포인트가 있으므로, 12 포인트 글꼴은 1/6 인치가 됩니다. 거기에 DPI 해상도인 160을 곱하면 약 27 장치 독립적 unit가 됩니다.

장치 독립적인 단위로 텍스트와 그 실제 숫자 point 사이즈를 표시해 주는 FontSizes라는 프로그램을 작성해 봅시다.

public class FontSizesPage : ContentPage
{
    public FontSizesPage()
    {
        BackgroundColor = Color.White;
        StackLayout stackLayout = new StackLayout
        {
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center
        };
        // Do the NamedSize values.
        NamedSize[] namedSizes =
        {
            NamedSize.Default,NamedSize.Micro,NamedSize.Small,
            NamedSize.Medium,NamedSize.Large
        };
        foreach (NamedSize namedSize in namedSizes)
        {
            double fontSize = Device.GetNamedSize(namedSize, typeof(Label));
            stackLayout.Children.Add(new Label
            {
                Text = String.Format("Named Size= {0} ({1:F2})", namedSize, fontSize),
                FontSize = fontSize,
                TextColor = Color.Black
            });
        }
        // Resolution in device-independent units per inch.
        double resolution = 160;
        // 가로 분리선
        stackLayout.Children.Add(
            new BoxView
            {
                Color = Color.Accent,
                HeightRequest = resolution / 80
            });
        // Do some numeric point sizes.
        int[] ptSizes = { 4681012 };
        foreach (double ptSize in ptSizes)
        {
            double fontSize = resolution * ptSize / 72;
            stackLayout.Children.Add(new Label
            {
                Text = String.Format("Point Size = {0} ({1:F2})", ptSize, fontSize),
                FontSize = fontSize,
                TextColor = Color.Black
            });
        }
        Content = stackLayout;
    }
}
cs

비교를 쉽게 하기 위해 텍스트는 검은 색, 배경은 흰색으로 통일 했으며, 두 개의 foreach 블럭 사이에 구분을 위해 BoxView를 넣었습니다. BoxView의 높이는 HeightRequest로 지정되며 1/8인치쯤 됩니다.

흥미로운 것은 명명된 사이즈보다도, 계산 결과에 따른 시각적 크기가 플랫폼간에 더 일관성이 있다는 점입니다. 괄호 안의 숫자는 장치 독립적인 단위의 숫자인 FontSize입니다.



Fitting text to available size


어떤 직사각형 내부에 텍스트를 맞추어야 할 때가 있습니다. 텍스트의 문자 수, 직사각형의 크기, 그리고 또다른 두 개의 숫자를 가지고서 LabelFontSize 속성 값을 계산할 수가 있습니다.

첫 번째 숫자는 줄간격입니다. 이것은 텍스트 한 줄당 Label 뷰의 세로 높이입니다. 기본 글꼴에서 줄간격은 FontSize 속성과 다음과 같은 대략적인 관계를 가집니다.

  • iOS : lineSpacing = 1.2 * label.FontSize
  • Android : lineSpacing = 1.2 * label.FontSize
  • Windows Phone : lineSpacing = 1.3 * label.FontSize


두 번째 숫자는 평균 글자 폭입니다. 기본 글꼴에 대문자와 소문자가 정상적으로 혼합되어있는 경우, 이 평균 문자 폭은 플랫폼에 관계없이 글꼴 크기의 약 절반입니다.

  • averageCharacterWidth = 0.5 * label.FontSize

예를 들어, 320 unit 폭에 80자짜리 텍스트 문자열을 맞추고, 글꼴 크기는 가능한 크게하고 싶다고 가정해 봅시다. 폭(320)을 문자 수의 절반(40)의 절반으로 나누면 LabelFontSize 속성으로 설정할 수 있는 글꼴 크기 8을 얻을 수 있습니다. 좀 불확실하고 테스트가 불가능한 경우엔 이 숫자를 좀 보수적으로 하면 안전합니다.

다음 프로그램은 줄 간격과 평균 문자 너비를 사용하여 텍스트를 페이지 너비에 맞추기 위한 프로그램입니다. 상태 표시 줄이 있는 iPhone 상단의 영역도 제외합니다. iOS 상태 표시 줄을 쉽게 제외하기 위해 ContentView를 사용했습니다.

ContentViewLayout에서 파생되지만, Layout에서 상속하는 내용에만 Content 속성을 추가합니다. ContentViewFrame의 기본 클래스이기도 합니다. ContentView는 직사각형 영역을 차지하는 기능밖에 없지만 두 가지 용도에서 유용한데 그것은 다른 view의 부모가 되어 내부에 새로운 custom view를 정의하는 용도이고, 다른 하나는 margin을 시뮬레이션하는 것입니다.

Xamarin.Forms에는 margin에 대한 개념이 없다는 것을 알아채셨는지 모르겠습니다. margin은 실제로는 padding과 비슷하지만 패딩은 view에 속한 것인 반면 padding은 view 외부에 있고 실제로는 부모 view의 일부입니다. ContentView를 사용하여 이것을 시뮬레이션 해볼 수 있습니다. view에 margin을 설정해야하는 경우 ContentView에 view를 배치하고 ContentViewPadding 속성을 설정하면 됩니다. ContentViewLayout으로부터 Padding을 상속받습니다.

EstimatedFontSize 프로그램은 약간 다른 방식으로 ContentView를 사용합니다. 이 프로그램은 iOS 상태 표시줄을 피하기 위해 페이지에 패딩을 설정하지만 ContentView를 해당 페이지의 내용으로 설정합니다. 따라서 ContentView는 페이지와 크기는 같지만 iOS 상태 표시 줄은 제외됩니다. SizeChanged 이벤트가 붙고 텍스트 글꼴 크기를 계산하는 데 사용되는 것이 이 ContenteView입니다.

SizeChanged 핸들러는 첫 번째 인수를 사용하여 이벤트를 실행하는 객체(여기서는 ContentView)를 가져옵니다. 계산은 주석에 설명되어 있습니다.

public class EstimatedFontSizePage : ContentPage
{
    Label label;
    public EstimatedFontSizePage()
    {
        label = new Label();
        Padding = new Thickness(0, Device.OnPlatform(2000), 00);
        ContentView contentView = new ContentView
        {
            Content = label
        };
        contentView.SizeChanged += OnContentViewSizeChanged;
        Content = contentView;
    }
    void OnContentViewSizeChanged(object sender, EventArgs args)
    {
        string text="A default system font with a font size S " + 
            "has a line height of about ({0:F1} * S) and an " + 
            "average character width of about ({1:F1} * S). " + 
            "On this page, which has a width of {2:F0} and a " + 
            "height of {3:F0}, a font size of ?1 should " + 
            "comfortably render the ??2 characters in this " + 
            "paragraph with ?3 lines and about ?4 characters " + 
            "per line. Does it work?";
        // Get View whose size is changing.
        View view = (View)sender;
        // Define two values as multiples of font size.
        double lineHeight = Device.OnPlatform(1.21.21.3);
        double charWidth = 0.5;
        // Format the  text and gets its character length.
        text = String.Format(text, lineHeight, charWidth, view.Width, view.Height);
        int charCount = text.Length;
        // Because:
        // lineCount = view.Height / (lineHeight * fontSize) 
        // charsPerLine = view.Width / (charWidth * fontSize) 
        // charCount = lineCount * charsPerLine 
        // Hence, solving for fontSize:
        int fontSize = (int)Math.Sqrt(view.Width * view.Height / 
            (charCount * lineHeight * charWidth));
        // Now these values can be calculated
        int lineCount = (int)(view.Height / (lineHeight * fontSize));
        int charsPerLine = (int)(view.Width / (charWidth * fontSize));
        // Replace the placeholders with thw values
        text = text.Replace("?1", fontSize.ToString());
        text = text.Replace("??2", charCount.ToString());
        text = text.Replace("?3", lineCount.ToString());
        text = text.Replace("?4", charsPerLine.ToString());
        // Set the Label properties
        label.Text = text;
        label.FontSize = fontSize;
    }
}
cs


"?1", "??2", "? ", "?4"라는 텍스트 placeholder는 그 자리에 올 숫자와 동일한 자리수의 고유한 문자로 선택되었습니다.

목표는 텍스트가 삐져 나오지 않는 한 가장 큰 사이즈로 만드는 것입니다.

나쁘지 않은 결과로군요. 세 플랫폼 모두에서 가장 최하단의 한줄이 비워져 있습니다만 계산 방법은 맞는 듯 합니다. 가로모드에서는 언제나 같은 FontSize로 계산되는 것은 아닙니다.




A fit-to-size clock


Device 클래스 내에는 주기적인 이벤트를 발생시키는 타이머를 설정할 수 있는 정적 메서드인 StartTimer가 있습니다.

Device.StartTimer의 첫 번째 인수는 TimeSpan 값으로 표시되는 간격입니다. 타이머는 해당 간격에 따라 주기적으로 이벤트를 실행합니다(최소 15~16 밀리초 까지 지정할 수 있는데, 이는 비디오 디스플레이 상에서 일반적으로 초당 60 프레임의 속도 주기에 해당합니다). 이벤트 핸들러에는 인수가 없지만 타이머를 계속 유지하려면 true를 반환해야합니다.

FitToSizeClock 프로그램은 시간을 표시용 레이블을 만들고 두 개의 이벤트를 설정합니다.
하나는 글꼴 크기를 변경을 위한 SizeChanged 이벤트이고, 다른 하나는 Text 속성을 변경하기위한 1 초 간격의 Device.StartTimer 이벤트입니다.

최근의 C#에서는 작은 이벤트 핸들러는 람다 함수로 처리하곤 합니다. 그러면 이벤트 처리 코드를 이벤트를 발생시키는 객체의 인스턴스화 및 초기화에 매우 가깝게 배치할 수 있습니다. 또한 이러한 객체를 필드로 저장하지 않고 이벤트 핸들러 내에서 참조할 수도 있습니다.

이 프로그램에서도 두 개의 이벤트 핸들러는 람다 함수로 처리되었습니다.
public class FitzToSizeClockPage : ContentPage
{
    public FitzToSizeClockPage()
    {
        Label clockLabel = new Label
        {
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center
        };
        Content = clockLabel;
        // Handle the SizeChanged event for the page
        SizeChanged += (object sender, EventArgs args) =>
          {
            // Scale the font size to the page width
            //       (based on 11 characters in the displayed string).
            if (this.Width > 0)
                  clockLabel.FontSize = this.Width / 6;
          };
        // Start the timer going.
        Device.StartTimer(TimeSpan.FromSeconds(1), () =>
        {
            // Set the Text property of the Label.
            clockLabel.Text = DateTime.Now.ToString("h:mm:ss tt");
            return true;
        });
    }
}
cs

StartTimer 핸들러는 10 또는 11자의 DateTime string format을 지정하는데, 그 중 두 개는 대문자이며 평균 문자폭보다 넓습니다. SizeChanged 핸들러는 글꼴 크기를 페이지 너비의 1/6로 설정하여 12자를 표시한다고 가정합니다.

당연히 가로 모드에서는 글자가 더 큽니다.

이 타이머는 정확하지 않으므로 표시된 시간은 동일한 장치의 다른 시간 표시와 정확히 일치하지 않을 수 있습니다. Tick을 좀더 자주 하게 만들면 더 정확하게 할 수 있습니다. 그렇게 한다 해도 디스플레이가 초당 한 번만 변경되고 그때까지는 새로운 레이아웃주기가 필요하지 않으므로 성능에 많은 영향을 미치지 않습니다.


Accessibility issues

EstimatedFontSize 프로그램과 FitToSizeClock 프로그램은 모두 미묘한 결함이 있지만, 장치의 접근성 기능을 사용하여 텍스트를 더 크게 만들 수 있는 사람이라면 아무 문제가 없습니다.

iOS에서는 설정(Settings)를 실행하고 일반(General), 손쉬운 사용(Accessibility) 및 더 큰 텍스트(Larger Text)를 선택합니다. 그런 다음 슬라이더를 사용하여 화면의 텍스트를 더 크게 또는 더 작게 만들 수 있습니다.

Android에서는 설정(Settings) 앱을 실행하고 디스플레이(Display)를 선택한 다음 글자 크기(Font Size)를 선택합니다. Small, Normal (기본값), Large 또는 Huge를 선택하기 위한 네 개의 버튼이 제공됩니다.

Windows 10 Mobile 장치에서는 설정(Setiings) 앱을 실행하고 액세스 용이성(Ease of Access)을 선택한 다음 추가 옵션(More Options)을 선택하십시오. 그런 다음 텍스트 크기 조정 슬라이더를 100 %에서 200 %로 이동할 수 있습니다.

여기서 알 수 있는 것이 있습니다:

iOS 설정은 Xamarin.Forms앱에 영향을 주지 못합니다.

Android 설정은 Device.GetNamedSize에서 반환된 값에 영향을줍니다. 크기가 Normal로 설정된 경우 NamedSize.Default는 원래와 같은 14를 반환하지만 Small인 경우 12, Large의 경우 16, Huge의 경우에는 18 1/3을 반환합니다.

또한 Android 화면에 표시된 모든 텍스트는 FontSize를 상수로 정한 경우에도 선택한 설정에 따라 크기가 작거나 커집니다.

Windows 10 Mobile의 경우, Device.GetNamedSize에서 반환된 값은 설정에 종속되지는 않지만 모든 텍스트가 더 크게 표시됩니다.

다시 말해, EstimatedFontSize 또는 FitToSizeClock 프로그램은 Android 또는 Windows 10 Mobile의 설정으로 더 큰 폰트크기를 사용하고자 해도 올바르게 실행되지 않고 텍스트의 일부가 잘리게 됩니다.

이 분제를 좀더 파고들어 보겠습니다. AccessibilityTest 프로그램은 페이지에 두 개의 Label 요소를 출력합니다. 첫 번째는 FontSize가 20이고, 두 번째는 단지 첫번째 라벨의 글꼴의 크기가 변할 때 그 크기만 출력합니다.


public class AccessobilityTestPage : ContentPage
    {
    public AccessobilityTestPage()
    {
        Label testLabel = new Label
        {
            Text = "FontSize of 20" + Environment.NewLine + "20 characters across",
            FontSize = 20,
            HorizontalTextAlignment = TextAlignment.Center,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.CenterAndExpand
        };
        Label displayLabel = new Label
        {
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.CenterAndExpand
        };
        testLabel.SizeChanged += (sender, args) =>
        {
            displayLabel.Text = String.Format("{0:F0} \u00D7 {1:F0}",
                testLabel.Width, testLabel.Height);
        };
        Content = new StackLayout
        {
            Children =
            {
                testLabel,
                displayLabel
            }
        };
    }
}
cs

보통의 경우 두 번째 Label은 앞에서 설명한 가정과 대략 일치하는 크기를 표시합니다.

그러나 이제 접근성 설정으로 들어가서 설정을 바꾸면 Android와 Windows 10 Mobile 모두 큰 텍스트를 표시합니다.

앞에서 설명한 문자 크기 가정이 더 이상 유효하지 않아 텍스트 크기를 맞출수 없게 됩니다.

그러나 직사각형 영역 안에서 텍스트 크기를 조정하는 다른 방법이 있습니다.

Empirically fitting text(경험적으로 맞는 텍스트)


그 또 다른 방법은 특정 글꼴 크기를 기반으로 렌더링된 텍스트의 크기를 일단 결정한 다음 해당 글꼴 크기를 위 또는 아래로 조정하는 경험적인 방법입니다. 이 방법은 접근성 설정에 관계없이 모든 장치에서 작업할 수 있다는 것이 장점입니다.

그러나 이 과정은 생각보다 까다로울 수 있는데, 그 첫 번째 문제는 글꼴 크기와 렌더링된 텍스트의 높이 사이에 명확한 선형 관계가 없다는 것입니다. 텍스트가 컨테이너의 폭에 비해 상대적으로 커지면 커질수록 점점 더 많은 줄 바꿈이 발생하여 더 많은 공간이 낭비됩니다. 최적 폰트 크기를 구하는 계산을 할 때는 값을 좁히는 루프과정이 종종 발생합니다.

두 번째 문제는 특정 글꼴 크기로 렌더링된 Label의 크기를 가져 오는 실제 메커니즘에 있습니다. LabelSizeChanged 핸들러를 설정할 수는 있지만 해당 핸들러에서 재귀 호출을 발생시키는 변경(예 : 새 FontSize 속성 설정)이 발생해서는 안됩니다.

따라서 더 나은 방법은 VisualElement에 의해 정의되고 Label과 다른 모든 view에 의해 상속되는 GetSizeRequest 메서드를 호출하는 것입니다. GetSizeRequest에는 두 개의 인수 (width constraint와 height constraint)가 필요합니다. 이 값은 출력하려는 요소들을 어떤 사이즈의 사각형에 맞추길 원하는가 하는 사이즈를 나타내며, 둘 중 하나 혹은 둘다 무한대로 설정할 수 있습니다. Label과 함께 GetSizeRequest를 사용할 때는 일반적으로 width 제약 조건 인수를 컨테이너 폭으로, height 제약 조건을 Double.PositiveInfinity로 설정합니다.

GetSizeRequest 메서드는 SizeRequest라는 구조체를 반환하는데, 그 구조체는 Size 형식의 Request, Minimum이라는 두 가지 속성으로 이루어집니다. Request 속성은 렌더링된 텍스트의 크기를 나타냅니다(26 장에서 다 자세히 다룰 예정입니다).

EmpiricalFontSize 프로젝트는 이 기술을 보여줍니다. 이 프로그램에서는 편의상 FontCalc라는 작은 구조체를 정의합니다. 이 구조체의 생성자는 특정 Label (이미 텍스트로 초기화 된), 폰트 사이즈 , 텍스트 너비에 대해 GetSizeRequest를 호출합니다.
struct FontCalc
{
    public double FontSize { get; set; }
    public double TextHeight { get; set; }
    public FontCalc(Label label, double fontSize, double containerWidth) : this()
    {
        // Save the font size
        FontSize = fontSize;
        // Recalculate the Label height
        label.FontSize = fontSize;
        SizeRequest sizeRequest = label.GetSizeRequest(containerWidth, 
            Double.PositiveInfinity);
        // Save the height
        TextHeight = sizeRequest.Request.Height;
    }
}
cs
렌더링 된 Label의 높이는 TextHeight 속성에 저장됩니다.

페이지나 레이아웃에서 GetSizeRequest를 호출하면, 페이지 또는 레이아웃에서 모든 children의 크기를 visual tree를 통해 가져와야합니다. 물론 이것은 성능 저하를 초래하므로 필요한 경우가 아니면 호출을 하지 않는 것이 좋습니다. 그러나 Label에는 자식이 없으므로 Label에서 GetSizeRequest를 호출하는 것이 그렇게까지 나쁘진 않습니다. 그래도 어쨌거나 호출을 최적화하는 편이 좋습니다. 컨테이너 크기를 초과하지 않는 최대 크기를 구하기 위해 loop를 여러번 반복하여 돌리지는 말길 바랍니다. 알고리즘적으로 최적값을 좁혀 들어가는 프로세스가 더 좋습니다.

GetSizeRequest를 사용할 때는, 그 요소가 시각적 트리의 일부여야 하고 레이아웃 프로세스가 부분적으로라도 시작되어야 합니다. 페이지 클래스의 생성자에서 GetSizeRequest를 호출해서는 안 됩니다(정보가 나오지 않을 겁니다). 첫 번째 합리적인 사용 위치는 페이지의 OnAppearing 메서드에서 override하는 것입니다.  물론, GetSizeRequest 그 단계에서는 메서드에 인수를 전달할 충분한 정보가 없을 수도 있습니다.

그러나 GetSizeRequest를 호출하는 것에는 어떤  부작용도 없습니다. 요소에 새 크기가 설정되지 않아 SizeChanged 이벤트가 발생하지 않으므로 SizeChanged 핸들러의 내부에서 호출해도 안전합니다.

EmpiricalFontSizePage 클래스는 Label을 호스팅하는 ContentViewSizeChanged 핸들러에서 FontCalc 값을 인스턴스화합니다. 각 FontCalc 값의 생성자는 Label 상에서 GetSizeRequest를 호출하고 결과값으로 TextHeight를 저장합니다.

SizeChanged 핸들러는 최적값의 범위를 이 10~100 사이로 보고 상하한값을 결정하는 과정을 시작합니다. 그래서 변수의 이름을 lowerFontCalcupperFontCalc로 정합니다.
public class EmpiricalFontSizePage : ContentPage
{
    Label label;
 
    public EmpiricalFontSizePage()
    {
        label = new Label();
 
        Padding = new Thickness(0, Device.OnPlatform(2000), 00);
        ContentView contentView = new ContentView
        {
            Content = label
        };
        contentView.SizeChanged += OnContentViewSizeChanged;
        Content = contentView;
    }
 
    void OnContentViewSizeChanged(object sender,EventArgs args)
    {
        // Get View whose size is changing.
        View view = (View)sender;
 
        if (view.Width <= 0 || view.Height <= 0)
            return;
 
        label.Text = "This is a paragraph of text displayed with " +
            "a FontSize value of ?? that is empirically " +
            "calculated in a loop within the SizeChanged " +
            "handler of the Label's container. This technique " +
            "can be tricky: You don't want to get into " +
            "an infinite loop by triggering a layout pass " +
            "with every calculation. Does it work?";
 
        // Calculate the height of the rendered text.
        FontCalc lowerFontCalc = new FontCalc(label, 10, view.Width);
        FontCalc upperFontCalc = new FontCalc(label, 100, view.Width);
 
        while (upperFontCalc.FontSize - lowerFontCalc.FontSize > 1)
        {
            // Get the average font size of the upper and lower bounds
            double fontSize = (lowerFontCalc.FontSize + upperFontCalc.FontSize) / 2;
 
            // Check the new text height against the container height.
            FontCalc newFontCalc = new FontCalc(label, fontSize, view.Width);
 
            if (newFontCalc.TextHeight > view.Height)
            {
                upperFontCalc = newFontCalc;
            }
            else
            {
                lowerFontCalc = newFontCalc;
            }
        }
 
        // Set the final font size and the text with the embedded value.
        label.FontSize = lowerFontCalc.FontSize;
        label.Text = label.Text.Replace("??", label.FontSize.ToString("F0"));
    }
}
cs

while 루프가 반복될 때마다 두 FontCalc 값의 FontSize 속성이 평균화되고 새 FontCalc값이 얻어집니다. 이렇게 얻어진 값은 렌더링된 텍스트의 높이에 따라 새로운 lowerFontCalc 또는 upperFontCalc 값이 됩니다. 계산된 글꼴 크기가 최적 의 1 unit 내에 있으면 루프가 끝납니다.

대략 7회 iteration이면 EstimatedFontSize 프로그램에서 계산된 예상값보다 좋은 값을 얻는 데 충분합니다.

전화를 옆으로 돌리면 유사한 과정을 거쳐 거의 비슷한 글꼴 크기로 나타납니다.


이 알고리듬은 단순히 평균값을 구하는 것 이상으로 향상될 수 있습니다. 그러나 글꼴 크기와 렌더링된 텍스트 높이 간의 관계는 조금 복잡하기 때문에 때로는 가장 간단한 방법이 좋을 수 있습니다.

댓글 없음:

댓글 쓰기