Xamarin.ios에서 사용자 지정 컨트롤 만들기Creating Custom Controls in Xamarin.Mac

Xamarin.ios 응용 프로그램 C# 에서 및 .net을 사용 하는 경우, SwiftXcode 에서 개발자가 작업 하는 것과 동일한 사용자 정의 컨트롤에 액세스할 수 있습니다.When working with C# and .NET in a Xamarin.Mac application, you have access to the same User Controls that a developer working in Objective-C, Swift and Xcode does. Xamarin.ios는 Xcode와 직접 통합 되므로 Xcode의 Interface Builder 를 사용 하 여 사용자 정의 컨트롤을 만들고 유지 관리 하거나 선택적으로 코드에서 C# 직접 만들 수 있습니다.Because Xamarin.Mac integrates directly with Xcode, you can use Xcode's Interface Builder to create and maintain your User Controls (or optionally create them directly in C# code).

MacOS는 다양 한 기본 제공 사용자 정의 컨트롤을 제공 하지만 사용자 지정 컨트롤을 만들어 사용자 지정 컨트롤을 만들어 사용자 지정 UI 테마 (예: 게임 인터페이스)와 사용자 지정 UI 테마를 일치 시 키 지 않도록 해야 할 수 있습니다.While macOS provides a wealth of built-in User Controls, there might be times that you need to create a custom control to provide functionality not provided out-of-the-box or to match a custom UI theme (such as a game interface).

이 문서에서는 Xamarin.ios 응용 프로그램에서 재사용 가능한 사용자 지정 사용자 인터페이스 컨트롤을 만드는 기본 사항을 다룹니다.In this article, we'll cover the basics of creating a reusable Custom User Interface Control in a Xamarin.Mac application. Hello, Mac 문서를 먼저 사용 하는 것이 가장 좋습니다. 특히 Xcode 및 Interface Builder콘센트 및 작업 섹션을 소개 하 고,에서 사용할 주요 개념 및 기술을 설명 하 고 있습니다. 이 문서를 참조 하세요.It is highly suggested that you work through the Hello, Mac article first, specifically the Introduction to Xcode and Interface Builder and Outlets and Actions sections, as it covers key concepts and techniques that we'll be using in this article.

Xamarin.ios 내부 문서의 목적에 따라 클래스/메서드 노출 C# 섹션을 살펴볼 수 있습니다. 여기에서는 C# 클래스를 목표에 연결 하는 데 사용 되는RegisterExport명령을 설명 합니다. 개체 및 UI 요소You may want to take a look at the Exposing C# classes / methods to Objective-C section of the Xamarin.Mac Internals document as well, it explains the Register and Export commands used to wire-up your C# classes to Objective-C objects and UI Elements.

사용자 지정 컨트롤 소개Introduction to Custom Controls

위에서 설명한 것 처럼, 다시 사용할 수 있는 사용자 지정 사용자 인터페이스 컨트롤을 만들어 Xamarin.ios 앱 UI에 고유한 기능을 제공 하거나 사용자 지정 UI 테마 (예: 게임 인터페이스)를 만들어야 하는 경우가 있을 수 있습니다.As stated above, there might be times when you need to create a reusable, Custom User Interface Control to provide unique functionality for your Xamarin.Mac app's UI or to create a custom UI theme (such as a game interface).

이러한 경우 NSControl에서 쉽게 상속 하 고 코드를 통해 C# 또는 Xcode의 Interface Builder를 통해 앱의 UI에 추가할 수 있는 사용자 지정 도구를 만들 수 있습니다.In these situations, you can easily inherit from NSControl and create a custom tool that can either be added to your app's UI via C# code or through Xcode's Interface Builder. NSControl에서 상속 하 여 사용자 지정 컨트롤에는 기본 제공 사용자 인터페이스 컨트롤의 모든 표준 기능 (예: NSButton)이 자동으로 포함 됩니다.By inheriting from NSControl your custom control will automatically have all of the standard features that a built-in User Interface Control has (such as NSButton).

사용자 지정 사용자 인터페이스 컨트롤이 사용자 지정 차트 및 그래픽 도구와 같은 정보를 표시 하는 경우 NSControl대신 NSView에서 상속 하는 것이 좋습니다.If your custom User Interface control just displays information (like a custom charting and graphic tool), you might want to inherit from NSView instead of NSControl.

사용 되는 기본 클래스에 관계 없이 사용자 지정 컨트롤을 만드는 기본 단계는 동일 합니다.No matter which base class is used, the basic steps for creating a custom control is the same.

이 문서에서는 고유한 사용자 인터페이스 테마를 제공 하는 사용자 지정 플립 스위치 구성 요소와 완전히 작동 하는 사용자 지정 사용자 인터페이스 컨트롤을 빌드하는 예제를 만듭니다.In this article will, create a custom Flip Switch component that provides a unique User Interface Theme and an example of building a fully functional Custom User Interface Control.

사용자 지정 컨트롤 빌드Building the Custom Control

만드는 사용자 지정 컨트롤은 사용자 입력에 응답 하 게 되므로 (마우스 왼쪽 단추 클릭) NSControl에서 상속 됩니다.Since the custom control we are creating will be responding to user input (left mouse button clicks), we are going to inherit from NSControl. 이러한 방식으로 사용자 지정 컨트롤에는 기본 제공 사용자 인터페이스 컨트롤의 모든 표준 기능이 자동으로 포함 되 고 표준 macOS 컨트롤과 같이 응답 합니다.In this way, our custom control will automatically have all of the standard features that a built-in User Interface Control has and respond like a standard macOS control.

Mac용 Visual Studio에서 사용자 지정 사용자 인터페이스 컨트롤을 만들 Xamarin.ios 프로젝트를 엽니다 (또는 새 항목을 만듭니다).In Visual Studio for Mac, open the Xamarin.Mac project that you want to create a Custom User Interface Control for (or create a new one). 새 클래스를 추가 하 고 NSFlipSwitch호출 합니다.Add a new class and call it NSFlipSwitch:

다음으로 NSFlipSwitch.cs 클래스를 편집 하 고 다음과 같이 만듭니다.Next, edit the NSFlipSwitch.cs class and make it look like the following:

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

    }
}

에서 사용자 지정 클래스에 대 한 첫 번째 작업은 NSControl에서 상속 하 고 Register 명령을 사용 하 여이 클래스를 객관적인 C 및 Xcode의 Interface Builder에 노출 한다는 것입니다.The first thing to notice about our custom class in that we are inheriting from NSControl and using the Register command to expose this class to Objective-C and Xcode's Interface Builder:

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

다음 섹션에서는 위의 코드의 나머지 부분에 대해 자세히 살펴보겠습니다.In the following sections, we'll take a look at the rest of the above code in detail.

컨트롤 상태 추적Tracking the Control's State

사용자 지정 컨트롤이 스위치 이므로 스위치의 설정/해제 상태를 추적 하는 방법이 필요 합니다.Since our Custom Control is a switch, we need a way to track the On/Off state of the switch. NSFlipSwitch에서 다음 코드를 사용 하 여이를 처리 합니다.We handle that with the following code in NSFlipSwitch:

private bool _value = false;
...

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

스위치의 상태가 변경 되 면 UI를 업데이트 하는 방법이 필요 합니다.When the state of the switch changes, we need a way to updated the UI. 이렇게 하려면 컨트롤에서 NeedsDisplay = true를 사용 하 여 UI를 다시 그리도록 합니다.We do that by forcing the control to redraw its UI with NeedsDisplay = true.

컨트롤이 단일 On/Off 상태 (예: 3 위치를 포함 하는 다중 상태 스위치)를 더 많이 필요로 하는 경우에는 열거형 을 사용 하 여 상태를 추적할 수 있습니다.If our control required more that a single On/Off state (for example a multi-state switch with 3 positions), we could have used an Enum to track the state. 이 예에서는 간단한 bool 을 사용할 수 있습니다.For our example, a simple bool will do.

또한 스위치 상태를 설정 및 해제 하는 도우미 메서드를 추가 했습니다.We also added a helper method to swap the state of the switch between On and Off:

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

나중에이 도우미 클래스를 확장 하 여 스위치 상태가 변경 되 면 호출자에 게 알립니다.Later, we'll expand this helper class to inform the caller when the switches state has changed.

컨트롤의 인터페이스 그리기Drawing the Control's Interface

핵심 그래픽 그리기 루틴을 사용 하 여 런타임에 사용자 지정 컨트롤의 사용자 인터페이스를 그릴 예정입니다.We are going to use Core Graphic drawing routines to draw our custom control's User Interface at runtime. 이렇게 하려면 먼저 컨트롤에 대 한 계층을 설정 해야 합니다.Before we can do this, we need to turn on layers for our control. 다음 개인 방법으로이 작업을 수행 합니다.We do this with the following private method:

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

이 메서드는 컨트롤이 적절 하 게 구성 되었는지 확인 하기 위해 각 컨트롤의 생성자에서 호출 됩니다.This method gets called from each of the control's constructors to ensure that the control is properly configured. 예를 들면,For example:

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

다음으로 DrawRect 메서드를 재정의 하 고 핵심 그래픽 루틴을 추가 하 여 컨트롤을 그려야 합니다.Next, we need to override the DrawRect method and add the Core Graphic routines to draw the control:

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

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

}

컨트롤의 상태가 변경 될 때 (예: On 에서 Off로 전환) 컨트롤에 대 한 시각적 표시를 조정 합니다.We'll be adjusting the visual representation for the control when its state changes (such as going from On to Off). 상태가 변경 될 때마다 NeedsDisplay = true 명령을 사용 하 여 컨트롤이 새 상태를 다시 그리도록 강제할 수 있습니다.Any time the state changes, we can use the NeedsDisplay = true command to force the control to redraw for the new state.

사용자 입력에 응답Responding to User Input

사용자 지정 컨트롤에 사용자 입력을 추가할 수 있는 두 가지 기본 방법은 마우스 처리 루틴 또는 제스처 인식기를 재정의 하는 것입니다.There are two basic way that we can add user input to our custom control: Override Mouse Handling Routines or Gesture Recognizers. 사용 되는 메서드는 컨트롤에 필요한 기능을 기반으로 합니다.Which method we use, will be based on the functionality required by our control.

중요

사용자가 만드는 모든 사용자 지정 컨트롤에 대해 재정의 메서드나 제스처 인식자를 사용 해야 하지만 서로 충돌할 수 있습니다.For any custom control you create, you should use either Override Methods or Gesture Recognizers, but not both at the same time as they can conflict with each other.

재정의 메서드를 사용 하 여 사용자 입력 처리Handling User Input with Override Methods

NSControl 또는 NSView에서 상속 되는 개체에는 마우스 또는 키보드 입력 처리를 위한 몇 가지 재정의 메서드가 있습니다.Objects that inherit from NSControl (or NSView) have several override methods for handling mouse or keyboard input. 예제 컨트롤의 경우 사용자가 왼쪽 마우스 단추를 사용 하 여 컨트롤을 클릭할 때 스위치 상태를 설정해제 합니다.For our example control, we want to flip the state of the switch between On and Off when the user clicks on the control with the left mouse button. 다음 재정의 메서드를 NSFlipSwitch 클래스에 추가 하 여이를 처리할 수 있습니다.We can add the following override methods to the NSFlipSwitch class to handle this:

#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 메서드에서 스위치의 On/Off 상태를 대칭 이동 합니다.In the above code, we call the FlipSwitchState method (defined above) to flip the On/Off state of the switch in the MouseDown method. 이렇게 하면 현재 상태를 반영 하도록 컨트롤을 강제로 다시 그릴 수도 있습니다.This will also force the control to be redrawn to reflect the current state.

제스처 인식기를 사용 하 여 사용자 입력 처리Handling User Input with Gesture Recognizers

필요에 따라 제스처 인식기를 사용 하 여 컨트롤과 상호 작용 하는 사용자를 처리할 수 있습니다.Optionally, you can use Gesture Recognizers to handle the user interacting with the control. 위에서 추가한 재정의를 제거 하 고 Initialize 메서드를 편집 하 여 다음과 같이 만듭니다.Remove the overrides added above, edit the Initialize method and make it look like the following:

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 메서드를 호출 하 여 사용자가 마우스 왼쪽 단추로 클릭 했을 때 스위치의 상태를 변경 합니다.Here, we are creating a new NSClickGestureRecognizer and calling our FlipSwitchState method to change the switch's state when the user clicks on it with the left mouse button. AddGestureRecognizer (click) 메서드는 제스처 인식기를 컨트롤에 추가 합니다.The AddGestureRecognizer (click) method adds the Gesture Recognizer to the control.

다시, 사용 하는 방법은 사용자 지정 컨트롤을 사용 하 여 수행 하려는 작업에 따라 달라 집니다.Again, which method we use depends on what we are trying to accomplish with our custom control. 사용자 상호 작용에 대 한 낮은 수준의 액세스 권한이 필요한 경우 재정의 메서드를 사용 합니다.If we need low level access the to user interaction, use the Override Methods. 마우스 클릭과 같이 미리 정의 된 기능을 사용 해야 하는 경우 제스처 인식기를 사용 합니다.If we need predefined functionality, such as mouse clicks, use Gesture Recognizers.

상태 변경 이벤트에 대 한 응답Responding to State Change Events

사용자가 사용자 지정 컨트롤의 상태를 변경 하는 경우 코드의 상태 변경에 응답 하는 방법이 필요 합니다 (예: 사용자 지정 단추를 클릭할 때 수행 하는 작업).When the user changes the state of our custom control, we need a way to respond to the state change in code (such as doing something when clicks on a custom button).

이 기능을 제공 하려면 NSFlipSwitch 클래스를 편집 하 고 다음 코드를 추가 합니다.To provide this functionality, edit the NSFlipSwitch class and add the following code:

#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 메서드를 편집 하 여 다음과 같이 만듭니다.Next, edit the FlipSwitchState method and make it look like the following:

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

먼저 사용자가 스위치의 상태를 변경할 때 작업을 수행할 수 있도록 코드 C# 에서 처리기를 추가할 수 있는 ValueChanged 이벤트를 제공 합니다.First, we provide a ValueChanged event that we can add a handler to in C# code so that we can perform an action when the user changes the state of the switch.

둘째, 사용자 지정 컨트롤은 NSControl에서 상속 되므로 Xcode의 Interface Builder에서 할당 될 수 있는 동작이 자동으로 포함 됩니다.Second, because our custom control inherits from NSControl, it automatically has an Action that can be assigned in Xcode's Interface Builder. 상태가 변경 될 때이 작업 을 호출 하려면 다음 코드를 사용 합니다.To call this Action when the state changes, we use the following code:

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

먼저 컨트롤이 컨트롤에 할당 되었는지 확인 합니다.First, we check to see if an Action has been assigned to the control. 그런 다음 작업을 정의 하는 경우이 작업 을 호출 합니다.Next, we call the Action if it has been defined.

사용자 지정 컨트롤 사용Using the Custom Control

사용자 지정 컨트롤을 완전히 정의한 상태에서 코드 또는 Xcode의 Interface Builder을 사용 하 여 C# xamarin.ios 앱의 UI에 추가할 수 있습니다.With our custom control fully defined, we can either add it to our Xamarin.Mac app's UI using C# code or in Xcode's Interface Builder.

Interface Builder를 사용 하 여 컨트롤을 추가 하려면 먼저 Xamarin.ios 프로젝트의 클린 빌드를 수행 하 고 Main.storyboard 파일을 두 번 클릭 하 여 편집을 위해 Interface Builder에서 엽니다.To add the control using Interface Builder, first do a clean build of the Xamarin.Mac project, then double-click the Main.storyboard file to open it in Interface Builder for edit:

다음으로 Custom View를 사용자 인터페이스 디자인으로 끌어 옵니다.Next, drag a Custom View into the User Interface design:

사용자 지정 보기를 선택한 상태에서 Id 검사기 로 전환 하 고 뷰의 클래스NSFlipSwitch로 변경 합니다.With the Custom View still selected, switch to the Identity Inspector and change the view's Class to NSFlipSwitch:

길잡이 편집기 로 전환 하 고 사용자 지정 컨트롤에 대 한 콘센트 를 만듭니다 (.m 파일이 아닌 ViewController.h 파일에 바인딩해야 함).Switch to the Assistant Editor and create an Outlet for the custom control (making sure to bind it in the ViewController.h file and not the .m file):

변경 내용을 저장 하 고 Mac용 Visual Studio로 돌아간 다음 변경 내용을 동기화 할 수 있습니다. ViewController.cs 파일을 편집 하 고 ViewDidLoad 메서드를 다음과 같이 만듭니다.Save your changes, return to Visual Studio for Mac and allow the changes to sync. Edit the ViewController.cs file and make the ViewDidLoad method look like the following:

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);
    };
}

여기서는 NSFlipSwitch 클래스에서 위에서 정의한 ValueChanged 이벤트에 응답 하 고 사용자가 컨트롤을 클릭할 때 현재 을 작성 합니다.Here, we respond to the ValueChanged event we defined above on the NSFlipSwitch class and write out the current Value when the user clicks on the control.

필요에 따라 Interface Builder로 돌아가서 컨트롤에 대 한 작업 을 정의할 수 있습니다.Optionally, we could return to Interface Builder and define an Action on the control:

다시 ViewController.cs 파일을 편집 하 고 다음 메서드를 추가 합니다.Again, edit the ViewController.cs file and add the following method:

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

중요

Interface Builder에서 이벤트 를 사용 하거나 작업 을 정의 해야 하지만 두 메서드를 동시에 사용 하거나 서로 충돌할 수 있습니다.You should use either the Event or define an Action in Interface Builder, but you should not use both methods at the same time or they can conflict with each other.

요약Summary

이 문서에서는 Xamarin.ios 응용 프로그램에서 재사용 가능한 사용자 지정 사용자 인터페이스 컨트롤을 만드는 방법에 대해 자세히 살펴봅니다.This article has taken a detailed look at creating a reusable Custom User Interface Control in a Xamarin.Mac application. 사용자 지정 컨트롤 UI를 그리는 방법, 마우스 및 사용자 입력에 응답 하는 두 가지 주요 방법 및 Xcode의 Interface Builder 작업에 새 컨트롤을 노출 하는 방법을 살펴보았습니다.We saw how to draw the custom controls UI, the two main ways to respond to mouse and user input and how to expose the new control to Actions in Xcode's Interface Builder.