Xamarin.Mac에서 사용자 지정 컨트롤 만들기

Xamarin.Mac 애플리케이션에서 C# 및 .NET으로 작업할 때 개발자가 작업하는 것과 동일한 사용자 컨트롤, SwiftXcodeObjective-C액세스할 수 있습니다. Xamarin.Mac은 Xcode와 직접 통합되므로 Xcode의 인터페이스 작성기를 사용하여 사용자 컨트롤을 만들고 기본(또는 필요에 따라 C# 코드에서 직접 만들기)할 수 있습니다.

macOS는 다양한 기본 제공 사용자 컨트롤을 제공하지만 기본적으로 제공되지 않는 기능을 제공하거나 사용자 지정 UI 테마(예: 게임 인터페이스)와 일치하도록 사용자 지정 컨트롤을 만들어야 하는 경우가 있을 수 있습니다.

Example of a custom UI control

이 문서에서는 Xamarin.Mac 애플리케이션에서 재사용 가능한 사용자 지정 사용자 인터페이스 컨트롤을 만드는 기본 사항을 설명합니다. 이 문서에서 사용할 주요 개념과 기술을 다루므로 Hello, Mac 문서, 특히 Xcode 및 인터페이스 작성기 및 콘센트 및 작업 소개 섹션을 통해 작업하는 것이 좋습니다.

Xamarin.Mac Internals 문서의 섹션에 Objective-C 대한 C# 클래스/메서드 노출을 살펴보고, Register C# 클래스 Objective-C 를 개체 및 UI 요소에 연결하는 데 사용되는 명령과 Export 설명합니다.

사용자 지정 컨트롤 소개

위에서 설명한 것처럼 다시 사용할 수 있는 사용자 지정 사용자 인터페이스 컨트롤을 만들어 Xamarin.Mac 앱의 UI에 고유한 기능을 제공하거나 사용자 지정 UI 테마(예: 게임 인터페이스)를 만들어야 하는 경우가 있을 수 있습니다.

이러한 상황에서는 C# 코드를 통해 또는 Xcode의 인터페이스 작성기를 통해 앱의 UI에 추가할 수 있는 사용자 지정 도구를 쉽게 상속 NSControl 하고 만들 수 있습니다. 사용자 지정 컨트롤에서 NSControl 상속하면 기본 제공 사용자 인터페이스 컨트롤에 있는 모든 표준 기능(예: NSButton)이 자동으로 포함됩니다.

사용자 지정 사용자 인터페이스 컨트롤에 사용자 지정 차트 및 그래픽 도구와 같은 정보만 표시되는 경우 대신 상속 NSViewNSControl할 수 있습니다.

어떤 기본 클래스를 사용하든 사용자 지정 컨트롤을 만드는 기본 단계는 동일합니다.

이 문서에서는 고유한 사용자 인터페이스 테마와 완벽하게 작동하는 사용자 지정 사용자 인터페이스 컨트롤을 빌드하는 예제를 제공하는 사용자 지정 Flip Switch 구성 요소를 만듭니다.

사용자 지정 컨트롤 빌드

만드는 사용자 지정 컨트롤은 사용자 입력(마우스 왼쪽 단추 클릭)에 응답하기 때문에 상속 NSControl됩니다. 이러한 방식으로 사용자 지정 컨트롤에는 기본 제공 사용자 인터페이스 컨트롤에 있는 모든 표준 기능이 자동으로 있으며 표준 macOS 컨트롤처럼 응답합니다.

Mac용 Visual Studio 사용자 지정 사용자 인터페이스 컨트롤을 만들려는 Xamarin.Mac 프로젝트를 엽니다(또는 새 사용자 인터페이스 컨트롤 만들기). 새 클래스를 추가하고 호출합니다.NSFlipSwitch

Adding a new class

다음으로, 클래스를 NSFlipSwitch.cs 편집하고 다음과 같이 표시합니다.

using Foundation;
using System;
using System.CodeDom.Compiler;
using AppKit;
using CoreGraphics;

namespace MacCustomControl
{
    [Register("NSFlipSwitch")]
    public class NSFlipSwitch : NSControl
    {
        #region Private Variables
        private bool _value = false;
        #endregion

        #region Computed Properties
        public bool Value {
            get { return _value; }
            set {
                // Save value and force a redraw
                _value = value;
                NeedsDisplay = true;
            }
        }
        #endregion

        #region Constructors
        public NSFlipSwitch ()
        {
            // Init
            Initialize();
        }

        public NSFlipSwitch (IntPtr handle) : base (handle)
        {
            // Init
            Initialize();
        }

        [Export ("initWithFrame:")]
        public NSFlipSwitch (CGRect frameRect) : base(frameRect) {
            // Init
            Initialize();
        }

        private void Initialize() {
            this.WantsLayer = true;
            this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
        }
        #endregion

        #region Draw Methods
        public override void DrawRect (CGRect dirtyRect)
        {
            base.DrawRect (dirtyRect);

            // Use Core Graphic routines to draw our UI
            ...

        }
        #endregion

        #region Private Methods
        private void FlipSwitchState() {
            // Update state
            Value = !Value;
        }
        #endregion

    }
}

Register 명령을 상속하고이 클래스를 Xcode의 인터페이스 작성기에서 NSControl 노출하는 사용자 지정 클래스 Objective-C 에 대해 가장 먼저 알아봅니다.

[Register("NSFlipSwitch")]
public class NSFlipSwitch : NSControl

다음 섹션에서는 위의 코드의 나머지 부분을 자세히 살펴보겠습니다.

컨트롤의 상태 추적

사용자 지정 컨트롤은 스위치이므로 스위치의 켜기/끄기 상태를 추적하는 방법이 필요합니다. 다음 코드로 처리합니다.NSFlipSwitch

private bool _value = false;
...

public bool Value {
    get { return _value; }
    set {
        // Save value and force a redraw
        _value = value;
        NeedsDisplay = true;
    }
}

스위치 상태가 변경되면 UI를 업데이트하는 방법이 필요합니다. 이렇게 하려면 컨트롤이 해당 UI NeedsDisplay = true를 다시 그리도록 합니다.

컨트롤에 단일 켜기/끄기 상태(예: 위치가 3인 다중 상태 스위치)가 더 필요한 경우 열거형을 사용하여 상태를 추적할 수 있습니다. 이 예제에서는 간단한 부울 이 수행됩니다.

또한 켜기 및 끄기 간에 스위치의 상태를 교환하는 도우미 메서드를 추가했습니다.

private void FlipSwitchState() {
    // Update state
    Value = !Value;
}

나중에 스위치 상태가 변경될 때 호출자에게 알리도록 이 도우미 클래스를 확장합니다.

컨트롤의 인터페이스 그리기

핵심 그래픽 그리기 루틴을 사용하여 런타임에 사용자 지정 컨트롤의 사용자 인터페이스를 그릴 예정입니다. 이 작업을 수행하려면 제어를 위해 레이어를 설정해야 합니다. 다음 프라이빗 메서드를 사용하여 이 작업을 수행합니다.

private void Initialize() {
    this.WantsLayer = true;
    this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
}

이 메서드는 컨트롤의 각 생성자에서 호출되어 컨트롤이 제대로 구성되었는지 확인합니다. 예시:

public NSFlipSwitch (IntPtr handle) : base (handle)
{
    // Init
    Initialize();
}

다음으로, 메서드를 재정의 DrawRect 하고 핵심 그래픽 루틴을 추가하여 컨트롤을 그려야 합니다.

public override void DrawRect (CGRect dirtyRect)
{
    base.DrawRect (dirtyRect);

    // Use Core Graphic routines to draw our UI
    ...

}

상태가 변경될 때(예: 켜기에서 기로) 컨트롤의 시각적 표현을 조정합니다. 상태가 변경될 때마다 이 NeedsDisplay = true 명령을 사용하여 컨트롤이 새 상태를 다시 그리도록 강제할 수 있습니다.

사용자 입력에 응답

사용자 지정 컨트롤에 사용자 입력을 추가할 수 있는 두 가지 기본 방법은 마우스 처리 루틴 또는 제스처 인식기 재정의입니다. 사용하는 방법은 컨트롤에 필요한 기능을 기반으로 합니다.

Important

만드는 모든 사용자 지정 컨트롤의 경우 메서드또는제스처 인식기를 재정의해야 하지만 둘 다 서로 충돌할 수 있으므로 동시에 사용하지 않아야 합니다.

재정의 메서드를 사용하여 사용자 입력 처리

상속 NSControl 되는 개체(또는 NSView)에는 마우스 또는 키보드 입력을 처리하기 위한 몇 가지 재정의 메서드가 있습니다. 예제 컨트롤의 경우 사용자가 왼쪽 마우스 단추로 컨트롤을 클릭할 때 켜기와 기 사이의 스위치 상태를 대칭 이동하려고 합니다. 다음 재정의 메서드를 클래스에 NSFlipSwitch 추가하여 이를 처리할 수 있습니다.

#region Mouse Handling Methods
// --------------------------------------------------------------------------------
// Handle mouse with Override Methods.
// NOTE: Use either this method or Gesture Recognizers, NOT both!
// --------------------------------------------------------------------------------
public override void MouseDown (NSEvent theEvent)
{
    base.MouseDown (theEvent);

    FlipSwitchState ();
}

public override void MouseDragged (NSEvent theEvent)
{
    base.MouseDragged (theEvent);
}

public override void MouseUp (NSEvent theEvent)
{
    base.MouseUp (theEvent);
}

public override void MouseMoved (NSEvent theEvent)
{
    base.MouseMoved (theEvent);
}
## endregion

위의 코드에서는 메서드(위에서 정의됨)를 호출 FlipSwitchState 하여 메서드에서 스위치 MouseDown 의 켜기/끄기 상태를 대칭 이동합니다. 이렇게 하면 현재 상태를 반영하도록 컨트롤을 다시 그려야 합니다.

제스처 인식기를 사용하여 사용자 입력 처리

필요에 따라 제스처 인식기를 사용하여 컨트롤과 상호 작용하는 사용자를 처리할 수 있습니다. 위에 추가된 재정의를 제거하고, 메서드를 Initialize 편집하고, 다음과 같이 표시합니다.

private void Initialize() {
    this.WantsLayer = true;
    this.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;

    // --------------------------------------------------------------------------------
    // Handle mouse with Gesture Recognizers.
    // NOTE: Use either this method or the Override Methods, NOT both!
    // --------------------------------------------------------------------------------
    var click = new NSClickGestureRecognizer (() => {
        FlipSwitchState();
    });
    AddGestureRecognizer (click);
}

여기서는 사용자가 NSClickGestureRecognizer 마우스 왼쪽 단추로 스위치를 클릭할 때 새 메서드를 만들고 메서드를 호출 FlipSwitchState 하여 스위치의 상태를 변경합니다. 이 메서드는 AddGestureRecognizer (click) 제스처 인식기를 컨트롤에 추가합니다.

다시 말하지만, 사용하는 방법은 사용자 지정 컨트롤을 사용하여 수행하려는 항목에 따라 달라집니다. 사용자 상호 작용에 대한 낮은 수준의 액세스가 필요한 경우 재정의 메서드를 사용합니다. 마우스 클릭과 같은 미리 정의된 기능이 필요한 경우 제스처 인식기를 사용합니다.

상태 변경 이벤트에 응답

사용자가 사용자 지정 컨트롤의 상태를 변경하는 경우 코드의 상태 변경에 응답하는 방법이 필요합니다(예: 사용자 지정 단추를 클릭할 때 수행).

이 기능을 제공하려면 클래스를 NSFlipSwitch 편집하고 다음 코드를 추가합니다.

#region Events
public event EventHandler ValueChanged;

internal void RaiseValueChanged() {
    if (this.ValueChanged != null)
        this.ValueChanged (this, EventArgs.Empty);

    // Perform any action bound to the control from Interface Builder
    // via an Action.
    if (this.Action !=null)
        NSApplication.SharedApplication.SendAction (this.Action, this.Target, this);
}
## endregion

다음으로, 메서드를 FlipSwitchState 편집하고 다음과 같이 표시합니다.

private void FlipSwitchState() {
    // Update state
    Value = !Value;
    RaiseValueChanged ();
}

먼저 사용자가 스위치의 상태를 변경할 때 작업을 수행할 수 있도록 C# 코드에 처리기를 추가할 수 있는 이벤트를 제공합니다 ValueChanged .

둘째, 사용자 지정 컨트롤이 NSControl상속되므로 Xcode의 인터페이스 작성기에서 할당할 수 있는 작업이 자동으로 있습니다. 상태가 변경되면 이 작업을 호출하려면 다음 코드를 사용합니다.

if (this.Action !=null)
    NSApplication.SharedApplication.SendAction (this.Action, this.Target, this);

먼저 컨트롤에 작업이 할당되었는지 확인하는 검사. 다음으로, 정의된 경우 Action호출합니다.

사용자 지정 컨트롤 사용

사용자 지정 컨트롤이 완전히 정의되면 C# 코드를 사용하거나 Xcode의 인터페이스 작성기에서 Xamarin.Mac 앱의 UI에 추가할 수 있습니다.

Interface Builder를 사용하여 컨트롤을 추가하려면 먼저 Xamarin.Mac 프로젝트의 클린 빌드를 수행한 다음 파일을 두 번 클릭하여 Main.storyboard 편집을 위해 Interface Builder에서 엽니다.

Editing the storyboard in Xcode

다음으로 사용자 인터페이스 디자인으로 끌어 Custom View 옵니다.

Selecting a Custom View from the Library

사용자 지정 보기를 계속 선택한 상태에서 ID 검사기로 전환하고 보기의 클래스를 다음으로 NSFlipSwitch변경합니다.

Setting the View's class

도우미 편집기로 전환하고 사용자 지정 컨트롤에 대한 콘센트를 만듭니다(파일이 아닌 .m 파일에 바인딩 ViewController.h 해야 합니다.)

Configuring a new Outlet

변경 내용을 저장하고, Mac용 Visual Studio 돌아가서 변경 내용이 동기화되도록 허용합니다. ViewController.cs 파일을 편집하고 메서드를 ViewDidLoad 다음과 같이 표시합니다.

public override void ViewDidLoad ()
{
    base.ViewDidLoad ();

    // Do any additional setup after loading the view.
    OptionTwo.ValueChanged += (sender, e) => {
        // Display the state of the option switch
        Console.WriteLine("Option Two: {0}", OptionTwo.Value);
    };
}

여기서는 클래스에서 ValueChangedNSFlipSwitch 위에서 정의한 이벤트에 응답하고 사용자가 컨트롤을 클릭할 때 현재 값을 기록합니다.

필요에 따라 인터페이스 작성기로 돌아가서 컨트롤에 대한 작업을 정의할 수 있습니다.

Configuring a new Action

다시 파일을 편집 ViewController.cs 하고 다음 메서드를 추가합니다.

partial void OptionTwoFlipped (Foundation.NSObject sender) {
    // Display the state of the option switch
    Console.WriteLine("Option Two: {0}", OptionTwo.Value);
}

Important

인터페이스 작성기에서 이벤트를 사용하거나 작업을 정의해야 하지만 두 메서드를 동시에 사용하면 안 되거나 서로 충돌할 수 있습니다.

요약

이 문서에서는 Xamarin.Mac 애플리케이션에서 재사용 가능한 사용자 지정 사용자 인터페이스 컨트롤을 만드는 방법에 대해 자세히 살펴보았습니다. 사용자 지정 컨트롤 UI를 그리는 방법, 마우스 및 사용자 입력에 응답하는 두 가지 기본 방법 및 Xcode의 인터페이스 작성기에서 작업에 새 컨트롤을 노출하는 방법을 알아보았습니다.