C#의 네이티브 뷰

Download Sample 샘플 다운로드

iOS, Android 및 UWP의 네이티브 뷰는 C#을 사용하여 만든 페이지에서 직접 참조 Xamarin.Forms 할 수 있습니다. 이 문서에서는 C#을 사용하여 만든 레이아웃에 네이티브 뷰를 Xamarin.Forms 추가하는 방법과 측정 API 사용량을 수정하기 위해 사용자 지정 보기의 레이아웃을 재정의하는 방법을 보여 줍니다.

개요

설정할 수 Content 있거나 컬렉션이 있는 컨트롤 Xamarin.Forms 은 Children 플랫폼별 보기를 추가할 수 있습니다. 예를 들어 iOS UILabel 를 속성 또는 StackLayout.Children 컬렉션에 ContentView.Content 직접 추가할 수 있습니다. 그러나 이 기능을 사용하려면 공유 프로젝트 솔루션에서 Xamarin.Forms 정의를 사용해야 #if 하며 .NET Standard 라이브러리 솔루션에서는 Xamarin.Forms 사용할 수 없습니다.

다음 스크린샷은 플랫폼별 보기가 다음에 추가된 것을 보여 줍니다 Xamarin.FormsStackLayout.

StackLayout Containing Platform-Specific Views

레이아웃에 플랫폼별 보기를 Xamarin.Forms 추가하는 기능은 각 플랫폼의 두 확장 메서드에 의해 활성화됩니다.

  • Add – 레이아웃 컬렉션에 Children 플랫폼별 보기를 추가합니다.
  • ToView – 플랫폼별 보기를 사용하여 컨트롤의 속성으로 Xamarin.FormsView 설정할 수 있는 뷰로 Content 래핑합니다.

공유 프로젝트에서 이러한 메서드를 Xamarin.Forms 사용하려면 적절한 플랫폼별 Xamarin.Forms 네임스페이스를 가져와야 합니다.

  • iOS – Xamarin.Forms.Platform.iOS
  • Android – Xamarin.Forms.Platform.Android
  • UWP(유니버설 Windows 플랫폼) – Xamarin.Forms.Platform.UWP

각 플랫폼에 플랫폼별 보기 추가

다음 섹션에서는 각 플랫폼의 레이아웃에 Xamarin.Forms 플랫폼별 보기를 추가하는 방법을 보여 줍니다.

iOS

다음 코드 예제에서는 a 및 ContentViewa UILabelStackLayout 에 추가하는 방법을 보여 줍니다.

var uiLabel = new UILabel {
  MinimumFontSize = 14f,
  Lines = 0,
  LineBreakMode = UILineBreakMode.WordWrap,
  Text = originalText,
};
stackLayout.Children.Add (uiLabel);
contentView.Content = uiLabel.ToView();

이 예제에서는 인스턴스와 contentView 인스턴스가 stackLayout 이전에 XAML 또는 C#에서 만들어졌다고 가정합니다.

Android

다음 코드 예제에서는 a 및 ContentViewa TextViewStackLayout 에 추가하는 방법을 보여 줍니다.

var textView = new TextView (MainActivity.Instance) { Text = originalText, TextSize = 14 };
stackLayout.Children.Add (textView);
contentView.Content = textView.ToView();

이 예제에서는 인스턴스와 contentView 인스턴스가 stackLayout 이전에 XAML 또는 C#에서 만들어졌다고 가정합니다.

유니버설 Windows 플랫폼

다음 코드 예제에서는 a 및 ContentViewa TextBlockStackLayout 에 추가하는 방법을 보여 줍니다.

var textBlock = new TextBlock
{
    Text = originalText,
    FontSize = 14,
    FontFamily = new FontFamily("HelveticaNeue"),
    TextWrapping = TextWrapping.Wrap
};
stackLayout.Children.Add(textBlock);
contentView.Content = textBlock.ToView();

이 예제에서는 인스턴스와 contentView 인스턴스가 stackLayout 이전에 XAML 또는 C#에서 만들어졌다고 가정합니다.

사용자 지정 보기에 대한 플랫폼 측정 재정의

각 플랫폼의 사용자 지정 보기는 디자인된 레이아웃 시나리오에 대한 측정값만 올바르게 구현하는 경우가 많습니다. 예를 들어 사용자 지정 보기는 디바이스의 사용 가능한 너비의 절반만 차지하도록 디자인되었을 수 있습니다. 그러나 다른 사용자와 공유된 후 사용자 지정 보기는 디바이스의 사용 가능한 전체 너비를 차지해야 할 수 있습니다. 따라서 레이아웃에서 다시 사용할 Xamarin.Forms 때 사용자 지정 뷰 측정 구현을 재정의해야 할 수 있습니다. 이러한 이유로 Add 확장 메서드는 ToView 측정 대리자를 지정할 수 있는 재정의를 제공하므로 레이아웃에 추가될 때 사용자 지정 보기 레이아웃을 재정의할 Xamarin.Forms 수 있습니다.

다음 섹션에서는 사용자 지정 보기의 레이아웃을 재정의하여 측정 API 사용량을 수정하는 방법을 보여 줍니다.

iOS

다음 코드 예제에서는 상속되는 클래스를 보여 CustomControl 줍니다.UILabel

public class CustomControl : UILabel
{
  public override string Text {
    get { return base.Text; }
    set { base.Text = value.ToUpper (); }
  }

  public override CGSize SizeThatFits (CGSize size)
  {
    return new CGSize (size.Width, 150);
  }
}

이 보기의 인스턴스는 다음 코드 예제에 설명된 대로 에 추가 StackLayout됩니다.

var customControl = new CustomControl {
  MinimumFontSize = 14,
  Lines = 0,
  LineBreakMode = UILineBreakMode.WordWrap,
  Text = "This control has incorrect sizing - there's empty space above and below it."
};
stackLayout.Children.Add (customControl);

그러나 재정의는 CustomControl.SizeThatFits 항상 높이 150을 반환하므로 다음 스크린샷과 같이 보기는 위와 아래에 빈 공간으로 표시됩니다.

iOS CustomControl with Bad SizeThatFits Implementation

이 문제에 대한 해결 방법은 다음 코드 예제에 설명된 대로 구현을 제공하는 GetDesiredSizeDelegate 것입니다.

SizeRequest? FixSize (NativeViewWrapperRenderer renderer, double width, double height)
{
  var uiView = renderer.Control;

  if (uiView == null) {
    return null;
  }

  var constraint = new CGSize (width, height);

  // Let the CustomControl determine its size (which will be wrong)
  var badRect = uiView.SizeThatFits (constraint);

  // Use the width and substitute the height
  return new SizeRequest (new Size (badRect.Width, 70));
}

이 메서드는 메서드에서 제공하는 너비를 CustomControl.SizeThatFits 사용하지만 높이 70으로 150의 높이를 대체합니다. 인스턴스가 CustomControl 추가StackLayout되면 메서드를 FixSize 클래스에서 제공하는 CustomControl 잘못된 측정값을 수정하기 위한 메서드로 GetDesiredSizeDelegate 지정할 수 있습니다.

stackLayout.Children.Add (customControl, FixSize);

이렇게 하면 다음 스크린샷과 같이 사용자 지정 보기가 위와 아래에 빈 공간 없이 올바르게 표시됩니다.

iOS CustomControl with GetDesiredSize Override

Android

다음 코드 예제에서는 상속되는 클래스를 보여 CustomControl 줍니다.TextView

public class CustomControl : TextView
{
  public CustomControl (Context context) : base (context)
  {
  }

  protected override void OnMeasure (int widthMeasureSpec, int heightMeasureSpec)
  {
    int width = MeasureSpec.GetSize (widthMeasureSpec);

    // Force the width to half of what's been requested.
    // This is deliberately wrong to demonstrate providing an override to fix it with.
    int widthSpec = MeasureSpec.MakeMeasureSpec (width / 2, MeasureSpec.GetMode (widthMeasureSpec));

    base.OnMeasure (widthSpec, heightMeasureSpec);
  }
}

이 보기의 인스턴스는 다음 코드 예제에 설명된 대로 에 추가 StackLayout됩니다.

var customControl = new CustomControl (MainActivity.Instance) {
  Text = "This control has incorrect sizing - it doesn't occupy the available width of the device.",
  TextSize = 14
};
stackLayout.Children.Add (customControl);

그러나 재정의는 CustomControl.OnMeasure 항상 요청된 너비의 절반을 반환하므로 다음 스크린샷과 같이 디바이스의 사용 가능한 너비의 절반만 차지하는 보기가 표시됩니다.

Android CustomControl with Bad OnMeasure Implementation

이 문제에 대한 해결 방법은 다음 코드 예제에 설명된 대로 구현을 제공하는 GetDesiredSizeDelegate 것입니다.

SizeRequest? FixSize (NativeViewWrapperRenderer renderer, int widthConstraint, int heightConstraint)
{
  var nativeView = renderer.Control;

  if ((widthConstraint == 0 && heightConstraint == 0) || nativeView == null) {
    return null;
  }

  int width = Android.Views.View.MeasureSpec.GetSize (widthConstraint);
  int widthSpec = Android.Views.View.MeasureSpec.MakeMeasureSpec (
    width * 2, Android.Views.View.MeasureSpec.GetMode (widthConstraint));
  nativeView.Measure (widthSpec, heightConstraint);
  return new SizeRequest (new Size (nativeView.MeasuredWidth, nativeView.MeasuredHeight));
}

이 메서드는 메서드에서 제공하는 너비를 CustomControl.OnMeasure 사용하지만 2를 곱합니다. 인스턴스가 CustomControl 추가StackLayout되면 메서드를 FixSize 클래스에서 제공하는 CustomControl 잘못된 측정값을 수정하기 위한 메서드로 GetDesiredSizeDelegate 지정할 수 있습니다.

stackLayout.Children.Add (customControl, FixSize);

그러면 다음 스크린샷과 같이 사용자 지정 보기가 디바이스 너비를 차지하여 올바르게 표시됩니다.

Android CustomControl with Custom GetDesiredSize Delegate

유니버설 Windows 플랫폼

다음 코드 예제에서는 상속되는 클래스를 보여 CustomControl 줍니다.Panel

public class CustomControl : Panel
{
  public static readonly DependencyProperty TextProperty =
    DependencyProperty.Register(
      "Text", typeof(string), typeof(CustomControl), new PropertyMetadata(default(string), OnTextPropertyChanged));

  public string Text
  {
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value.ToUpper()); }
  }

  readonly TextBlock textBlock;

  public CustomControl()
  {
    textBlock = new TextBlock
    {
      MinHeight = 0,
      MaxHeight = double.PositiveInfinity,
      MinWidth = 0,
      MaxWidth = double.PositiveInfinity,
      FontSize = 14,
      TextWrapping = TextWrapping.Wrap,
      VerticalAlignment = VerticalAlignment.Center
    };

    Children.Add(textBlock);
  }

  static void OnTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
  {
    ((CustomControl)dependencyObject).textBlock.Text = (string)args.NewValue;
  }

  protected override Size ArrangeOverride(Size finalSize)
  {
      // This is deliberately wrong to demonstrate providing an override to fix it with.
      textBlock.Arrange(new Rect(0, 0, finalSize.Width/2, finalSize.Height));
      return finalSize;
  }

  protected override Size MeasureOverride(Size availableSize)
  {
      textBlock.Measure(availableSize);
      return new Size(textBlock.DesiredSize.Width, textBlock.DesiredSize.Height);
  }
}

이 보기의 인스턴스는 다음 코드 예제에 설명된 대로 에 추가 StackLayout됩니다.

var brokenControl = new CustomControl {
  Text = "This control has incorrect sizing - it doesn't occupy the available width of the device."
};
stackLayout.Children.Add(brokenControl);

그러나 재정의는 CustomControl.ArrangeOverride 항상 요청된 너비의 절반을 반환하므로 다음 스크린샷과 같이 보기가 디바이스의 사용 가능한 너비의 절반으로 잘립니다.

UWP CustomControl with Bad ArrangeOverride Implementation

이 문제에 대한 해결 방법은 다음 코드 예제에 설명된 대로 뷰를 StackLayout추가할 때 구현을 제공하는 ArrangeOverrideDelegate 것입니다.

stackLayout.Children.Add(fixedControl, arrangeOverrideDelegate: (renderer, finalSize) =>
{
    if (finalSize.Width <= 0 || double.IsInfinity(finalSize.Width))
    {
        return null;
    }
    var frameworkElement = renderer.Control;
    frameworkElement.Arrange(new Rect(0, 0, finalSize.Width * 2, finalSize.Height));
    return finalSize;
});

이 메서드는 메서드에서 제공하는 너비를 CustomControl.ArrangeOverride 사용하지만 2를 곱합니다. 그러면 다음 스크린샷과 같이 사용자 지정 보기가 디바이스 너비를 차지하여 올바르게 표시됩니다.

UWP CustomControl with ArrangeOverride Delegate

요약

이 문서에서는 C#을 사용하여 만든 레이아웃에 네이티브 뷰를 Xamarin.Forms 추가하는 방법과 사용자 지정 보기의 레이아웃을 재정의하여 측정 API 사용량을 수정하는 방법을 설명했습니다.