전체 페이지뷰

2017년 2월 22일 수요일

Chapter 5. Dealing with sizes, part 1

다양한 시각 요소들에 연결되는 사이즈에 대한 참고사항을 이미 몇 가지 배웠습니다.
  • iOS 상태 표시줄의 높이는 20이며, 페이지의 Padding으로 조정 가능
  • BoxView의 width, height의 디폴트는 40
  • Frame의 디폴트 Padding은 20.
  • StackLayout의 디폴트 Spacing 속성은 6
 그리고, NamedSize라는 열거형으로서 Label 또는 ButtonFontSize 값에 적절한 platform-dependent한 값을 반환해 주는 Device.GetNamedSize가 있습니다.

이 숫자들, 그리고 그 단위는 대체 무엇입니까? 그리고 어떻게 하면 숫자를 필요로 하는 다른 크기관련 속성들을 현명하게 설정할 수 있겠습니까? 

이미 보아 왔듯 여러 종류의 플랫폼들은 각기 다른 스크린 사이즈, 텍스트 사이즈들을 갖고 있습니다. 그 텍스트의 양이라는 것은 Xamarin.Forms App이 예상하거나 제어할 수 있는 것일까요? 가능하다고 한다면 어떤 프로그래밍 연습이 필요한가요? 어플리케이션이 화면에서 원하는 텍스트 밀도를 얻기 위해서는 글꼴 크기를 조정해야합니까?

일반적으로 Xamarin.Forms 애플리케이션을 프로그래밍 할때는 시각적 객체에 정확한 수치를 구현하는 것은 옳지 않으며, 자마린과 개별 디바이스의 디폴트 값을 믿는것이 낫습니다.

그러나 가끔씩 프로그래머가 수치와 그것이 화면에 나타나는 양상을 아는 것이 좋을 때가 있습니다.

Pixels, points, dps, DIPs, DIUs


비디오 디스플레이는 직사각형 모양 픽셀 배열로 구성됩니다. 화면에 표시된 모든 물체는 픽셀 크기를 가집니다. PC의 초창기에 프로그래머들은 픽셀 단위로 물체를 표현하고 위치 시켰습니다. 그러나 스크린 사이즈나 픽셀의 밀도가 다양해짐에 따라, 수많은 디바이스에서 같아보이는 어플리케이션을 작성하기가 힘들어졌습니다. 따라서 다른 해결책이 필요해 졌습니다.

이 해결책은 먼저 데스크탑의 OS에서 시작되었고 그 이후 모바일 기기에 적용되었습니다. 그런 이유로 데스크탑에서부터 이 모험을 시작해볼까 합니다.

데스크탑용 비디오 디스플레이는 무용지물에 가까운 640 × 480에서부터 수천 개에 이르기까지 광범위한 픽셀 크기를 가지고 있습니다. 화면 비율 4 : 3이 컴퓨터 디스플레이 및 영화에서 TV에 이르기까지 표준이었지만, 현재는 16 : 9 (또는 16:10)의 고화질 종횡비가 더 일반적입니다.

데스크탑 비디오 디스플레이는 또한 일반적으로 화면의 대각선을 따라 인치 또는 센티미터로 측정된 물리적 치수를가집니다. 픽셀 치수를 실제 치수와 결합하면, 비디오 디스플레이의 해상도 또는 픽셀 밀도를 인치당 도트 수(DPI)로 계산할 수 있으며, 인치당 픽셀 수(PPI)로 표시하기도 합니다. 디스플레이 해상도는 인접한 픽셀 중심 간의 거리를 밀리미터 단위로 측정한 도트 피치(dot pitch)로도 측정 할 수 있습니다.

만약 800×600 크기의 고대 모니터의 대각선을 피타고라스의 정리를 사용하여 계산하면 1000이 됩니다. 이 모니터의 대각선 길이가 13 인치이면 픽셀 밀도가 77 DPI이거나 도트 피치가 0.33 밀리미터로 계산됩니다. 그러나 현대 노트북의 13 인치 화면은 2560 × 1600의 픽셀 크기에 이르고, 계산하면 약 230 DPI의 픽셀 밀도 또는 약 0.11 밀리미터의 도트 피치를 가집니다. 그래서 이 화면에서 100 픽셀 사각형 물체는,  800×600화면의 동일한 물체 크기의 1/3입니다.

프로그래머들은 시각적 요소를 정확한 사이즈로 표현하는데에 아주 애를 먹었습니다. 그런 이유로, Apple과 Microsoft는 프로그래머가 픽셀 대신, 독립적인 단위로 비디오 디스플레이를 사용할 수 있게 해주는 데 컴퓨팅 시스템을 고안했습니다. 프로그래머들이 만나게 되는 장치 독립적인 단위인 unit이 바로 그것입니다. unit과 pixel 간의 변환은 OS가 담당하게 됩니다.

애플의 세계에서, 데스크탑 비디오 디스플레이는 전통적으로 인치당 72 unit의 해상도를 가진다고 가정했습니다. 이 72라는 숫자는 활자인쇄(typography)로부터 유래한 것으로, 고전적 타이포그래피에서는 활자를 다루는데에 필요한 해상도를 대략적으로 인치당 72 point, 디지털 타이포그래피에서는 정확히 인치당 72 point로 표준화된 수치로 사용했습니다. 이 point가 unit이라고 생각하면 보다 직관적으로 시각적 객체의 크기를 가늠할 수가 있습니다.

Windows 환경에서도 device-independent pixel (DIP) 또는 device-independent unit (DIU)이라 불리는 애플과 유사한 기술이 개발되었습니다. 윈도우즈 프로그래머에게 데스크탑 디스플레이는 96DIU의 해상도를 가지는데, 이 수치는 애플의 것보다 정확히 1/3이 크긴 하지만사용자에 의해 조정 가능합니다.

그러나 모바일 디스플레이는 좀 다른 법칙을 가집니다. 현대 모바일 폰의 픽셀 밀도는 통상 데스크탑의 것보다 훨씬 높습니다. 따라서 그 밀도로 텍스트나 시각 객체를 표현하면 읽을 수 없을 정도로 크기가 작아질 수 있습니다.

전화기는 보통 랩탑이나 데스크탑에 비해 훨씬 얼굴 가까이에서 사용합니다. 그 차이에 의해 전화기에 표시된 객체는 데스크탑, 랩탑의 것에 비해 훨씬 작게 표시되어도 괜찮습니다. 전화기 화면이 작기 때문에 그렇게 축소하는 편이 더 많은 것을 화면에 표시할 수 있다는 점에서 바람직 합니다.

애플은 이 장치 독립적인 단위를 point라고 부르기 시작합니다. 최근까지, Retina라 불리는 Apple의 고밀도 디스플레이는 2 픽셀을 1 포인트로 변환합니다. 이는 MacBook Pro, iPad 및 iPhone에서 마찬가지인데, 최근의 iPhone6 plus는 3 픽셀을 1point로 합니다.

예를 들어, iPhone 4의 3.5 인치 화면인 640 × 960 픽셀 크기는 약 320 DPI의 실제 픽셀 밀도를 가집니다. 한 point에 두 pixel이 있으므로 iPhone 4에서 실행되는 앱 화면 크기는 320 × 480 포인트처럼 보여집니다. iPhone 3의 픽셀 크기는 실제로 320 × 480이고, 따라서 1 포인트는 1 픽셀과 같아서, 이 두 장치에서 실행되는 프로그램의 디스플레이 크기는 동일하게 표시됩니다. 물론 크기는 동일하나 iPhone3보다는 iPhone4의 해상도가 큽니다.

iPhone 3 및 iPhone 4의 경우, 스크린 크기와 point 크기간의 관계를 나타내는 변환계수는 데스크탑 표준 인 72보다 큰 인치당 160 포인트가 됩니다.

iPhone 5의 경우 화면 크기는 4 인치이지만 픽셀 크기는 640 × 1136이며, 픽셀 밀도는 iPhone 4와 거의 같습니다. 프로그램에게 이 화면의 크기는 320 × 768 point로 나타납니다.

iPhone 6의 화면 크기는 4.7 인치이며 픽셀 크기는 750 × 1334이고, 픽셀 밀도는 약 320 DPI입니다. 1 point에 두 개의 픽셀이 있으므로 프로그램에게 화면 크기는 375 × 667입니다.

그러나 iPhone6 Plus는 5.5 인치 화면과 픽셀 크기가 1080 × 1920으로, 400 DPI의 픽셀 밀도를 가집니다. 이 높은 픽셀 밀도는 1 포인트에 더 많은 픽셀이 있음을 의미하며, iPhone 6 Plus의 경우 Apple은 1 point를 3 pixel로 설정했습니다. 이는 일반적으로 360 × 640 포인트의 화면 크기를 의미하지만, 프로그램에겐  iPhone 6 Plus 스크린의 포인트 크기는 414 × 736이므로 감지된 해상도는 인치당 약 150 포인트입니다.

아래 표에 이 정보를 정리해 두었습니다.

안드로이드도 비슷합니다. 안드로이드 장치들은 사이즈와 픽셀크기가 매우 다양하지만, 프로그래머들은 dps(density-independent pixels)라는 단위로 작업합니다. 픽셀과 dps의 관계는 인치당 160 dps라고 가정하여 설정되는데, 즉 Apple 및 Android 의 device-independent 단위가 유사하다는 뜻입니다.

Microsoft는 윈도우폰 7에서 다른 방식의 접근을 합니다. 오리지널 Windows Phone 7 장치의 화면 크기는 480 x 800 픽셀이고, 이를 WVGA(Wide Video Graphics Array)라고 부릅니다. 여기서 디스플레이는 픽셀단위로 처리되었습니다. 480 × 800 Windows Phone 7 장치에서 평균 화면 크기가 4 인치라고 가정하면, Windows Phone 7은 약 240 DPI의 픽셀 밀도를 나타내게 되는 겁니다. 이것은 Android, iPhone에 비해 1.5배 높은 픽셀밀도입니다. 결과적으로 768 × 1280 (WXGA), 720x1280 (720p) 및 1080x1920 (1080p)이라는 몇 가지 큰 화면 크기가 허용되었습니다. 이 장치들에서 프로그래머는 장치독립적인 단위를 사용했는데 픽샐과 장치독립적 단위 간에 내부 요소를 계산하여 세로모드(portrait mode)에서 언제나 480p를 맞추도록 했습니다.

Windows Phone 8.1에서는 Windows Runtime API를 사용하여 화면의 픽셀 크기와 화면의 실제 크기를 기반으로 다양한 크기 조정 요소가 도입되었습니다. 다음 표는 WhatSize라는 프로그램(이제 곧 보여드릴 것입니다)을 사용하여 Windows Phone 8.1 에뮬레이터 기반으로 작성되었습니다.

WhatSize 프로그램에 표시된 DIU 단위의 높이가 Windows Phone 상태 표시줄을 제외하기 때문에 배율 인수는 너비로 계산됩니다. 최종 DPI 수치는 전체 픽셀 크기, 화면의 대각선 크기 (인치) 및 배율 인수를 기준으로 계산되었습니다. WVGA 4인치를 제외하면, 계산 된 DPI는 iOS 및 Android 디바이스와 관련된 160 DPI 기준과 거의 동일합니다.

Windows 10 Mobile은 다소 높은 배율 인수를 사용하며 0.2가 아닌 0.25의 배수로 사용됩니다. 다음 표는 Windows 10 Mobile 에뮬레이터를 기반으로 한 것입니다.

Windows 10 Mobile의 평균 DPI는 144정도로 160보다 작음을 알 수 있습니다

Xamarin.Forms는 가능한 한 낮은 사양의 플랫폼 규칙을 사용하도록 하는 철학을 가집니다. Xamarin.Forms 프로그래머는 각 플랫폼별로 이 규칙에 따라 정의된 크기로 작업합니다. 앞으로 Xamarin.Forms API를 통해 만나는 크기는 모두 platform-specific, device-independent한 것이며, 일반적으로 다음과 같은 해상도로 디스플레이를 다룹니다.

  • 인치당 160unit
  • 센티미터당 64unit
VisualElement 클래스는 WidthHeight, 두 속성을 정의하고, 그 속성들은 view, layout, page에서 장치독립적 단위로 렌더링할 크기를 제공합니다. 그러나 일단 WidthHeight의 초기 설정은 "mock"값 -1입니다. 이 속성의 값은 페이지의 모든 것의 사이즈와 위치가 정해지고 나서야 유효해집니다. 또한 HorizontalOptions 또는 VerticalOptions의 옵션을 기본 Fill 로 두었을 때, Fill이 아닌 경우보다 많은 공간을 차지하게 하는 경우가 있다는 것을 기억하기 바랍니다. WidthHeight 값은 이 초과분의 공간도 반영합니다. WidthHeight 값은 설정될 수 있는 모든 Padding까지 포함하며, BackgroundColor 속성으로 채색되는 영역과 일치합니다.

VisualElement는 시각적 요소의 Width 또는 Height 속성이 변경될 때에 발생하는 SizeChanged라는 이벤트를 정의합니다. 이 이벤트는 페이지가 배치될 때 발생하는 알림으로, 페이지의 크기와 위치 변경시 발생합니다. 이 프로세스는 대개 생성자에 의해 페이지가 처음 정의될 때 처음 발생하고, 레이아웃에 변경사항이 생길 때(예를 들어 ContentPageStackLayout에 뷰가 추가될 때, 또는 객체가 제거될 때, 시각요소의 속성을 변화시켰을 때) 발생합니다.

세로모드-가로모드 변환과 같은 때처럼 스크린 사이즈가 변화해도 발생합니다.

Xamarin.Forms에 좀더 익숙해지면 Layout<View>를 상속받아 작업을 할 일이 생길 겁니다. 그 과정은 26장, "Custom Layout"에서 배우기로 하고, 그때까진 단순히 Width, Height 속성이 언제 변화하는지만 알아도 충분합니다. 페이지 자체를 포함하여 페이지의 모든 시각적 개체에 SizeChanged handler를 연결할 수 있습니다. WhatSize 프로그램은 페이지의 크기를 가져와서 표시하는 방법을 보여드릴 겁니다.

public class WhatSizePage : ContentPage
    {
    Label label;
    public WhatSizePage()
    {
        label = new Label
        {
            FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label)),
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center
        };
        Content = label;
        SizeChanged += OnPageSizeChanged;
    }
    void OnPageSizeChanged(object sender, EventArgs args)
    {
        label.Text = String.Format("{0} \u00D7 {1}", Width, Height);
    }
}
cs


이것은 이 책에서 소개하는 첫번째 event handling 프로그램이며, 방식은 일반적 C# .NET의 방법과 동일합니다. 생성자 아래의 OnPageSizeChanged 이벤트 핸들러를 페이지의 SizeChanged 이벤트에 연결합니다. 이벤트 핸들러(일반적으로 sender라 명명합니다)의 첫 번째 인수는 이벤트를 발생시키는 개체 (이 경우 WhatSizePage의 인스턴스)이지만 이벤트 핸들러는 이를 사용하지 않습니다. 또한 이벤트 핸들러는 이벤트에 대한 추가 정보를 제공하는 두 번째 인수 (이른바 event arguments) 역시 사용하지 않습니다.

대신 이벤트 핸들러는 Label 요소 (편리하게 필드로 저장된)에 액세스하여 페이지의 WidthHeight 속성을 표시합니다. String.Format 내부에 사용된 유니코드는 곱하기(×) 표시입니다.

SizeChanged 이벤트로만 요소 크기를 얻을수 있는 것은 아닙니다. VisualElement는 시각적 요소에 크기가 지정되는 시기를 나타내는 OnSizeAllocated라는 protected virtual 메서드를 정의합니다. SizeChanged 대신 이 메소드를 오버라이드하는 것도 가능하지만, 이따금 크기가 변하지 않았을 때에도 호출되는 경우가 있습니다.

세 개의 표준 플랫폼에서 실행된 결과는 다음과 같습니다.

이 세 개 스크린 정보는,

  • iPhone 6 시뮬레이터, 픽셀크기 750 × 1334
  • LG Nexus 5, 픽셀크기 1080 x 1920
  • Nokia Lumia 925, 픽셀크기 768 × 1280

Android에서 프로그램이 인식하는 수직 크기에는 상태 표시줄과 하단 버튼이 차지하는 영역이 포함되지 않습니다. Windows 10 Mobile 장치의 세로 크기에는 상태 표시 줄이 차지하는 영역이 포함되지 않습니다.

세 플랫폼 모두 장치 방향 변경에 반응합니다. 화면을 반시계 방향으로 90도 돌리면 화면 크기가 다음과 같이 표시됩니다.

Android의 픽셀 너비 598에는 버튼 영역이 제외되며, 픽셀 높이 335는 항상 페이지 위에 나타나는 상태 표시줄을 제외합니다. Windows 10 Mobile 장치에서 픽셀 너비 728은 상태 표시줄의 영역이 제외됩니다.이  상태 표시줄은 같은 위치에 표시되지만 아이콘이 새 방향에 맞춰 회전됩니다.

픽셀크기 2048×1536인 iPad Air 2에서 실행한 화면입니다.

크기 배율 팩터(scailing factor)는 2이고, 대각선 9.7인치, DPI는 132입니다.

Surface Pro 3는 2160×1440 픽셀크기입니다. 크기 배율 팩터는 사용자가 조절하여 물체를 크거나 작게 표현 가능하긴 하지만 권장사항은 1.5입니다.

WhatSize가 표시하는 높이는 화면 하단의 작업 표시줄을 제외합니다. 화면은 144 DPI 해상도, 대각선 12인치 입니다.

WhatSize 프로그램에 있어 몇 가지 참고 사항입니다:

WhatSize는 생성자에서 단일 Label을 만들고 이벤트 핸들러에서 Text 속성을 설정하는데, 꼭 그렇게 작성해야 하는 것은 아닙니다. SizeChanged 핸들러를 사용하여 새로운 텍스트로 Label을 만들고 페이지의 Content로 새 Label을 설정할 수도 있는데, 이 경우 이전 Label은 더 이상 참조되지 않으므로 가비지 수집 대상이 됩니다. 그러나 새로운 시각적 요소를 만드는 것은 이 프로그램의 경우 낭비입니다. 프로그램은 하나의 Label 뷰만 만들고, 페이지의 새 크기를 표시할때는 Text 속성을 설정하는 것이 가장 좋습니다.

Xamarin.Forms 앱이 플랫폼별 정보 없이 방향 변경을 감지할 수있는 유일한 방법은 크기 변경을 모니터링하는 것입니다. 너비가 높이보다 크다면 가로모드(landscape)이고 반대의 경우는 세로모드(portrait)입니다.

Xamarin.Forms 솔루션은 Visual Studio이거나 Xamarin Studio이거나 관계없이 방향변화를 허용하는 것이 default 값입니다. 만약 어느 방향으로만 고정하고 싶다면 그렇게 할 수도 있습니다.

iOS의 경우, 솔루션 탐색기 상에서 info.plist를 더블클릭하여 나오는 창의 iPhone Deployment InfoSupported Device Orientations 항목을 사용하면 변경 가능합니다.

Android의 경우, MainActivity.cs 파일의 MainActivity 클래스에 있는 Activity Attribute에 다음 코드를 추가하십시오.

ScreenOrientation = ScreenOrientation.Landscape

또는

ScreenOrientation = ScreenOrientation.Portrait

솔루션 템플릿에 의해 생성 된 Activity 특성에는 이미 화면 방향을 참조하는 ConfigurationChanges 인수가 포함되어 있지만, ConfigurationChanges의 목적은 휴대폰의 방향이나 화면 크기가 변경될 때 작업을 다시 시작하지 못하도록 하는 것입니다.

Windows Phone 프로젝트의 경우, 사용할 클래스와 열거형은 Windows.Graphics.Display 네임 스페이스에 있습니다. MainPage.xaml.cs 파일의 MainPage 생성자에서 정적 DisplayInformation.AutoRotationPreferences 속성을 C# 비트 OR 연산과 결합된 DisplayOrientations 열거형의 하나 이상의 멤버로 설정합니다. 프로그램을 가로, 혹은 세로로 고정하기 위해서 다음의 코드를 추가합니다.

DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;

또는

DisplayInformation.AutoRotationPreferences = DisplayOrientations.Portrait;

댓글 없음:

댓글 쓰기