최신 macOS 앱 빌드

이 문서에서는 개발자가 Xamarin.Mac에서 최신 macOS 앱을 빌드하는 데 사용할 수 있는 몇 가지 팁, 기능 및 기술에 대해 설명합니다.

현대적인 전망을 사용하여 현대적인 디자인 빌드

최신 디자인에는 아래 표시된 예제 앱과 같은 최신 창 및 도구 모음 모양이 포함됩니다.

An example of a modern Mac app UI

전체 크기 콘텐츠 보기 사용

Xamarin.Mac 앱에서 이러한 모양을 구현하기 위해 개발자는 전체 크기 콘텐츠 보기를 사용하려고 합니다. 즉, 콘텐츠가 도구 및 제목 표시줄 영역에서 확장되고 macOS에서 자동으로 흐리게 표시됩니다.

코드에서 이 기능을 사용하도록 설정하려면 사용자 지정 클래스를 NSWindowController 만들어 다음과 같이 만듭니다.

using System;
using Foundation;
using AppKit;

namespace MacModern
{
    public partial class MainWindowController : NSWindowController
    {
        #region Constructor
        public MainWindowController (IntPtr handle) : base (handle)
        {
        }
        #endregion

        #region Override Methods
        public override void WindowDidLoad ()
        {
            base.WindowDidLoad ();

            // Set window to use Full Size Content View
            Window.StyleMask = NSWindowStyle.FullSizeContentView;
        }
        #endregion
    }
}

이 기능은 창을 선택하고 전체 크기 콘텐츠 보기를 검사 Xcode의 인터페이스 작성기에서 사용하도록 설정할 수도 있습니다.

Editing the main storyboard in Xcode's Interface Builder

전체 크기 콘텐츠 뷰를 사용하는 경우 개발자는 특정 콘텐츠(예: 레이블)가 그 아래로 미끄러지지 않도록 제목 및 도구 모음 영역 아래의 콘텐츠를 오프셋해야 할 수 있습니다.

이 문제를 복잡하게 하기 위해 타이틀 및 도구 모음 영역은 사용자가 현재 수행하고 있는 작업, 사용자가 설치한 macOS 버전 및/또는 앱이 실행 중인 Mac 하드웨어에 따라 동적 높이를 가질 수 있습니다.

따라서 사용자 인터페이스를 배치할 때 오프셋을 하드 코딩하기만 하면 작동하지 않습니다. 개발자는 동적 접근 방식을 취해야 합니다.

Apple은 코드에서 현재 콘텐츠 영역을 가져오기 위해 클래스의 NSWindow Key-Value ObservableContentLayoutRect 속성을 포함했습니다. 개발자는 이 값을 사용하여 콘텐츠 영역이 변경되면 필요한 요소를 수동으로 배치할 수 있습니다.

더 나은 솔루션은 자동 레이아웃 및 크기 클래스를 사용하여 코드 또는 인터페이스 작성기에서 UI 요소를 배치하는 것입니다.

다음 예제와 같은 코드를 사용하여 앱의 뷰 컨트롤러에서 AutoLayout 및 크기 클래스를 사용하여 UI 요소를 배치할 수 있습니다.

using System;
using AppKit;
using Foundation;

namespace MacModern
{
    public partial class ViewController : NSViewController
    {
        #region Computed Properties
        public NSLayoutConstraint topConstraint { get; set; }
        #endregion

        ...

        #region Override Methods
        public override void UpdateViewConstraints ()
        {
            // Has the constraint already been set?
            if (topConstraint == null) {
                // Get the top anchor point
                var contentLayoutGuide = ItemTitle.Window?.ContentLayoutGuide as NSLayoutGuide;
                var topAnchor = contentLayoutGuide.TopAnchor;

                // Found?
                if (topAnchor != null) {
                    // Assemble constraint and activate it
                    topConstraint = topAnchor.ConstraintEqualToAnchor (topAnchor, 20);
                    topConstraint.Active = true;
                }
            }

            base.UpdateViewConstraints ();
        }
        #endregion
    }
}

이 코드는 레이블(ItemTitle)에 적용되는 최상위 제약 조건에 대한 스토리지를 만들어 제목 및 도구 모음 영역에서 미끄러지지 않도록 합니다.

public NSLayoutConstraint topConstraint { get; set; }

개발자는 뷰 컨트롤러의 메서드를 재정의 UpdateViewConstraints 하여 필요한 제약 조건이 이미 빌드되었는지 테스트하고 필요한 경우 만들 수 있습니다.

새 제약 조건을 작성 ContentLayoutGuide 해야 하는 경우 제한해야 하는 컨트롤 창의 속성에 액세스하여 다음으로 NSLayoutGuide캐스팅됩니다.

var contentLayoutGuide = ItemTitle.Window?.ContentLayoutGuide as NSLayoutGuide;

TopAnchor 속성에 NSLayoutGuide 액세스하고 사용 가능한 경우 원하는 오프셋 양으로 새 제약 조건을 빌드하는 데 사용되며 새 제약 조건이 활성화되어 적용됩니다.

// Assemble constraint and activate it
topConstraint = topAnchor.ConstraintEqualToAnchor (topAnchor, 20);
topConstraint.Active = true;

간소화된 도구 모음 사용

일반 macOS 창에는 창의 위쪽 가장자리에 따라 실행되는 표준 제목 표시줄이 포함됩니다. 창에 도구 모음도 포함되어 있으면 이 제목 표시줄 영역 아래에 표시됩니다.

A standard Mac Toolbar

간소화된 도구 모음을 사용하면 제목 영역이 사라지고 도구 모음이 창 닫기, 최소화 및 최대화 단추와 함께 제목 표시줄의 위치로 위로 이동합니다.

A streamlined Mac Toolbar

간소화된 도구 모음은 메서드를 재정의 NSViewController 하고 ViewWillAppear 다음과 같이 표시함으로써 활성화됩니다.

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

    // Enable streamlined Toolbars
    View.Window.TitleVisibility = NSWindowTitleVisibility.Hidden;
}

이 효과는 일반적으로 지도, 일정, 메모 및 시스템 기본 설정과 같은 Shoebox 애플리케이션(하나의 창 앱)에 사용됩니다.

액세서리 뷰 컨트롤러 사용

앱의 디자인에 따라 개발자는 타이틀/도구 모음 영역 바로 아래에 표시되는 액세서리 보기 컨트롤러로 제목 표시줄 영역을 보완하여 현재 사용 중인 활동에 따라 사용자에게 상황에 맞는 컨트롤을 제공할 수도 있습니다.

An example Accessory View Controller

접근자 보기 컨트롤러는 개발자의 개입 없이 시스템에서 자동으로 흐리게 표시되고 크기가 조정됩니다.

액세서리 뷰 컨트롤러를 추가하려면 다음을 수행합니다.

  1. 편집하기 위해 솔루션 탐색기에서 Main.storyboard 파일을 두 번 클릭하여 엽니다.

  2. 사용자 지정 뷰 컨트롤러창의 계층 구조로 끌어옵니다.

    Adding a new Custom View Controller

  3. 액세서리 뷰의 UI 레이아웃:

    Designing the new view

  4. 액세서리 뷰를 UI에 대한 콘센트 및 기타 작업 또는 콘센트로 노출합니다 .

    Adding the required OUtlet

  5. 변경 내용을 저장합니다.

  6. 변경 내용을 동기화하려면 Mac용 Visual Studio 돌아갑니다.

편집하고 NSWindowController 다음과 같이 표시합니다.

using System;
using Foundation;
using AppKit;

namespace MacModern
{
    public partial class MainWindowController : NSWindowController
    {
        #region Constructor
        public MainWindowController (IntPtr handle) : base (handle)
        {
        }
        #endregion

        #region Override Methods
        public override void WindowDidLoad ()
        {
            base.WindowDidLoad ();

            // Create a title bar accessory view controller and attach
            // the view created in Interface Builder
            var accessoryView = new NSTitlebarAccessoryViewController ();
            accessoryView.View = AccessoryViewGoBar;

            // Set the location and attach the accessory view to the
            // titlebar to be displayed
            accessoryView.LayoutAttribute = NSLayoutAttribute.Bottom;
            Window.AddTitlebarAccessoryViewController (accessoryView);

        }
        #endregion
    }
}

이 코드의 핵심 요소는 뷰가 인터페이스 작성기에서 정의되고 출선으로 노출된 사용자 지정 보기로 설정되는 위치입니다.

accessoryView.View = AccessoryViewGoBar;

LayoutAttribute 그리고 액세서리가 표시될 위치를 정의하는 위치는 다음과 같습니다.

accessoryView.LayoutAttribute = NSLayoutAttribute.Bottom;

이제 macOS가 완전히 지역화 Left 되었으므로 속성 및 RightNSLayoutAttribute 속성은 더 이상 사용되지 않으며 대체되어야 LeadingTrailing합니다.

탭 창 사용

또한 macOS 시스템은 앱의 창에 액세서리 뷰 컨트롤러를 추가할 수 있습니다. 예를 들어 여러 앱의 Windows가 하나의 가상 창으로 병합되는 Tabbed Windows를 만들려면 다음을 수행합니다.

An example of a tabbed Mac Window

일반적으로 개발자는 Xamarin.Mac 앱에서 Tabbed Windows를 사용하여 제한된 작업을 수행해야 합니다. 시스템은 다음과 같이 자동으로 처리합니다.

  • 메서드가 호출될 때 Windows는 OrderFront 자동으로 Tabbed가 됩니다.
  • 메서드가 호출되면 Windows가 OrderOut 자동으로 Untabbed됩니다.
  • 코드에서 모든 탭 창은 여전히 "표시"로 간주되지만, 맨 앞에 있지 않은 탭은 CoreGraphics를 사용하여 시스템에서 숨겨집니다.
  • Windows를 TabbingIdentifier Tabs로 그룹화하려면 이 NSWindow 속성을 사용합니다.
  • 기반 앱인 NSDocument 경우 개발자 작업 없이 이러한 기능 중 몇 가지가 자동으로 사용하도록 설정됩니다(예: 탭 표시줄에 추가되는 더하기 단추).
  • 비기NSDocument반 앱은 탭 그룹의 "더하기" 단추를 사용하여 메서드를 재정의NSWindowsController하여 GetNewWindowForTab 새 문서를 추가할 수 있습니다.

시스템 기반 Tabbed Windows를 사용하려는 앱은 모두 AppDelegate 다음과 같이 표시될 수 있습니다.

using AppKit;
using Foundation;

namespace MacModern
{
    [Register ("AppDelegate")]
    public class AppDelegate : NSApplicationDelegate
    {
        #region Computed Properties
        public int NewDocumentNumber { get; set; } = 0;
        #endregion

        #region Constructors
        public AppDelegate ()
        {
        }
        #endregion

        #region Override Methods
        public override void DidFinishLaunching (NSNotification notification)
        {
            // Insert code here to initialize your application
        }

        public override void WillTerminate (NSNotification notification)
        {
            // Insert code here to tear down your application
        }
        #endregion

        #region Custom Actions
        [Export ("newDocument:")]
        public void NewDocument (NSObject sender)
        {
            // Get new window
            var storyboard = NSStoryboard.FromName ("Main", null);
            var controller = storyboard.InstantiateControllerWithIdentifier ("MainWindow") as NSWindowController;

            // Display
            controller.ShowWindow (this);
        }
        #endregion
    }
}

여기서 속성은 NewDocumentNumber 만든 새 문서의 수를 추적하고 메서드는 NewDocument 새 문서를 만들어 표시합니다.

그러면 NSWindowController 다음과 같이 표시할 수 있습니다.

using System;
using Foundation;
using AppKit;

namespace MacModern
{
    public partial class MainWindowController : NSWindowController
    {
        #region Application Access
        /// <summary>
        /// A helper shortcut to the app delegate.
        /// </summary>
        /// <value>The app.</value>
        public static AppDelegate App {
            get { return (AppDelegate)NSApplication.SharedApplication.Delegate; }
        }
        #endregion

        #region Constructor
        public MainWindowController (IntPtr handle) : base (handle)
        {
        }
        #endregion

        #region Public Methods
        public void SetDefaultDocumentTitle ()
        {
            // Is this the first document?
            if (App.NewDocumentNumber == 0) {
                // Yes, set title and increment
                Window.Title = "Untitled";
                ++App.NewDocumentNumber;
            } else {
                // No, show title and count
                Window.Title = $"Untitled {App.NewDocumentNumber++}";
            }
        }
        #endregion

        #region Override Methods
        public override void WindowDidLoad ()
        {
            base.WindowDidLoad ();

            // Prefer Tabbed Windows
            Window.TabbingMode = NSWindowTabbingMode.Preferred;
            Window.TabbingIdentifier = "Main";

            // Set default window title
            SetDefaultDocumentTitle ();

            // Set window to use Full Size Content View
            // Window.StyleMask = NSWindowStyle.FullSizeContentView;

            // Create a title bar accessory view controller and attach
            // the view created in Interface Builder
            var accessoryView = new NSTitlebarAccessoryViewController ();
            accessoryView.View = AccessoryViewGoBar;

            // Set the location and attach the accessory view to the
            // titlebar to be displayed
            accessoryView.LayoutAttribute = NSLayoutAttribute.Bottom;
            Window.AddTitlebarAccessoryViewController (accessoryView);

        }

        public override void GetNewWindowForTab (NSObject sender)
        {
            // Ask app to open a new document window
            App.NewDocument (this);
        }
        #endregion
    }
}

여기서 정적 App 속성 AppDelegate은 . 이 메서드는 SetDefaultDocumentTitle 새로 만든 문서의 수에 따라 새 문서 제목을 설정합니다.

다음 코드는 앱이 탭을 사용하는 것을 선호한다는 것을 macOS에 알리고 앱의 Windows를 탭으로 그룹화할 수 있는 문자열을 제공합니다.

// Prefer Tabbed Windows
Window.TabbingMode = NSWindowTabbingMode.Preferred;
Window.TabbingIdentifier = "Main";

다음 재정의 메서드는 사용자가 클릭할 때 새 문서를 만드는 더하기 단추를 탭 표시줄에 추가합니다.

public override void GetNewWindowForTab (NSObject sender)
{
    // Ask app to open a new document window
    App.NewDocument (this);
}

핵심 애니메이션 사용

코어 애니메이션은 macOS에 기본 제공되는 고성능 그래픽 렌더링 엔진입니다. 코어 애니메이션은 CPU에서 그래픽 작업을 실행하는 대신 최신 macOS 하드웨어에서 사용할 수 있는 GPU(그래픽 처리 장치)를 활용하도록 최적화되어 있어 컴퓨터 속도가 느려질 수 있습니다.

핵심 애니메이션에서 제공하는 이 CALayer작업은 빠르고 유동적인 스크롤 및 애니메이션과 같은 작업에 사용할 수 있습니다. 핵심 애니메이션을 완전히 활용하려면 앱의 사용자 인터페이스를 여러 하위 보기 및 계층으로 구성해야 합니다.

개체는 CALayer 다음과 같이 사용자에게 화면에 표시되는 내용을 개발자가 제어할 수 있도록 하는 몇 가지 속성을 제공합니다.

  • Content - 레이어의 내용을 제공하는 또는 해당 형식일 수 NSImageCGImage 있습니다.
  • BackgroundColor - 레이어의 배경색을 CGColor
  • BorderWidth - 테두리 너비를 설정합니다.
  • BorderColor - 테두리 색을 설정합니다.

앱의 UI에서 핵심 그래픽을 활용하려면 레이어 지원 보기를 사용해야 합니다. Apple은 개발자가 항상 창의 콘텐츠 보기에서 사용하도록 설정해야 한다고 제안합니다. 이렇게 하면 모든 자식 보기도 레이어 백업을 자동으로 상속합니다.

또한 Apple은 시스템에서 필요한 몇 가지 설정(예: 망막 디스플레이에 필요한 설정)을 자동으로 처리하므로 새 보기를 하위 계층으로 추가하는 CALayer 것이 아니라 레이어 지원 뷰를 사용할 것을 제안합니다.

코어 애니메이션 계층을 검사 Xcode의 trueNSView 인터페이스 작성기 내부를 뷰 효과 검사기에서 설정 WantsLayer 하여 레이어 백업을 사용하도록 설정할 수 있습니다.

The View Effects Inspector

레이어를 사용하여 뷰 다시 그리기

Xamarin.Mac 앱에서 계층 지원 뷰를 사용할 때의 또 다른 중요한 단계는 에 있는 받는 사람의 설정을 LayerContentsRedrawPolicyNSView 지정하는 것입니다 OnSetNeedsDisplayNSViewController. 예시:

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

    // Set the content redraw policy
    View.LayerContentsRedrawPolicy = NSViewLayerContentsRedrawPolicy.OnSetNeedsDisplay;
}

개발자가 이 속성을 설정하지 않으면 성능상의 이유로 필요하지 않은 프레임 원본이 변경될 때마다 뷰가 다시 그려집니다. 그러나 이 속성을 개발자로 OnSetNeedsDisplay 설정하면 콘텐츠를 강제로 다시 그리도록 true 수동으로 설정 NeedsDisplay 해야 합니다.

보기가 더티 표시되면 시스템에서 보기의 속성을 검사WantsUpdateLayer. 이 메서드가 반환 true 되면 메서드가 UpdateLayer 호출되고, 그렇지 않으면 DrawRect View의 메서드가 호출되어 뷰의 콘텐츠를 업데이트합니다.

Apple에는 필요한 경우 보기 콘텐츠를 업데이트하기 위한 다음과 같은 제안이 있습니다.

  • Apple은 성능이 크게 향상되므로 가능하면 언제든지 사용하는 UpdateLaterDrawRect 것을 선호합니다.
  • layer.Contents 비슷한 UI 요소에 같게 사용합니다.
  • 또한 Apple은 가능하면 개발자가 표준 보기(예: 다시)를 NSTextField사용하여 UI를 작성하는 것을 선호합니다.

사용 UpdateLayer하려면 해당 클래스에 대한 사용자 지정 클래스를 NSView 만들고 코드를 다음과 같이 표시합니다.

using System;
using Foundation;
using AppKit;

namespace MacModern
{
    public partial class MainView : NSView
    {
        #region Computed Properties
        public override bool WantsLayer {
            get { return true; }
        }

        public override bool WantsUpdateLayer {
            get { return true; }
        }
        #endregion

        #region Constructor
        public MainView (IntPtr handle) : base (handle)
        {
        }
        #endregion

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

        }

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

            // Draw view
            Layer.BackgroundColor = NSColor.Red.CGColor;
        }
        #endregion
    }
}

최신 끌어서 놓기 사용

사용자에게 최신 끌어서 놓기 환경을 제공하려면 개발자는 앱의 끌어서 놓기 작업에서 끌어서 놓기 작업을 채택해야 합니다. 끌어서 놓기(Drag Flocking)는 처음에 끌어오고 있는 각 개별 파일 또는 항목이 사용자가 끌기 작업을 계속함에 따라 무리가 묶이는 개별 요소(항목 수의 개수로 커서 아래에 그룹화)로 나타나는 위치입니다.

사용자가 끌기 작업을 종료하면 개별 요소의 잠금을 해제하고 원래 위치로 돌아갑니다.

다음 예제 코드는 사용자 지정 보기에서 Flocking 끌기를 사용하도록 설정합니다.

using System;
using System.Collections.Generic;
using Foundation;
using AppKit;

namespace MacModern
{
    public partial class MainView : NSView, INSDraggingSource, INSDraggingDestination
    {
        #region Constructor
        public MainView (IntPtr handle) : base (handle)
        {
        }
        #endregion

        #region Override Methods
        public override void MouseDragged (NSEvent theEvent)
        {
            // Create group of string to be dragged
            var string1 = new NSDraggingItem ((NSString)"Item 1");
            var string2 = new NSDraggingItem ((NSString)"Item 2");
            var string3 = new NSDraggingItem ((NSString)"Item 3");

            // Drag a cluster of items
            BeginDraggingSession (new [] { string1, string2, string3 }, theEvent, this);
        }
        #endregion
    }
}

각 항목을 배열에서 별도의 요소로 끌어 BeginDraggingSessionNSView 서 메서드로 전송하여 무리 효과를 달성했습니다.

또는 NSOutlineView클래스 NSTableView 를 사용하여 작업할 때 클래스의 NSTableViewDataSource 메서드를 사용하여 PastboardWriterForRow 끌기 작업을 시작합니다.

using System;
using System.Collections.Generic;
using Foundation;
using AppKit;

namespace MacModern
{
    public class ContentsTableDataSource: NSTableViewDataSource
    {
        #region Constructors
        public ContentsTableDataSource ()
        {
        }
        #endregion

        #region Override Methods
        public override INSPasteboardWriting GetPasteboardWriterForRow (NSTableView tableView, nint row)
        {
            // Return required pasteboard writer
            ...

            // Pasteboard writer failed
            return null;
        }
        #endregion
    }
}

이를 통해 개발자는 모든 행을 붙여넣기에 단일 그룹으로 쓰는 이전 메서드 WriteRowsWith 와 달리 끌어서 놓는 테이블의 모든 항목에 대해 개별 NSDraggingItem 항목을 제공할 수 있습니다.

작업할 NSCollectionViews때 끌기가 PasteboardWriterForItemAt 시작될 때 메서드와 반대로 WriteItemsAt 메서드를 다시 사용합니다.

개발자는 항상 큰 파일을 붙여넣지 않도록 해야 합니다. macOS Sierra 를 새로 추가한 파일 약속 은 개발자가 사용자가 새 NSFilePromiseProvider 클래스와 NSFilePromiseReceiver 클래스를 사용하여 Drop 작업을 완료할 때 나중에 처리될 붙여넣기에서 지정된 파일에 대한 참조를 배치할 수 있도록 합니다.

최신 이벤트 추적 사용

제목 또는 도구 모음 영역에 추가된 사용자 인터페이스 요소(예: NSButtona)의 경우 사용자는 요소를 클릭하고 팝업 창 표시와 같이 정상적으로 이벤트를 발생하도록 할 수 있어야 합니다. 그러나 항목이 제목 또는 도구 모음 영역에 있으므로 사용자는 요소를 클릭하고 끌어 창도 이동할 수 있어야 합니다.

코드에서 이 작업을 수행하려면 요소에 대한 사용자 지정 클래스(예: NSButton)를 만들고 다음과 같이 이벤트를 재정의 MouseDown 합니다.

public override void MouseDown (NSEvent theEvent)
{
    var shouldCallSuper = false;

    Window.TrackEventsMatching (NSEventMask.LeftMouseUp, 2000, NSRunLoop.NSRunLoopEventTracking, (NSEvent evt, ref bool stop) => {
        // Handle event as normal
        stop = true;
        shouldCallSuper = true;
    });

    Window.TrackEventsMatching(NSEventMask.LeftMouseDragged, 2000, NSRunLoop.NSRunLoopEventTracking, (NSEvent evt, ref bool stop) => {
        // Pass drag event to window
        stop = true;
        Window.PerformWindowDrag (evt);
    });

    // Call super to handle mousedown
    if (shouldCallSuper) {
        base.MouseDown (theEvent);
    }
}

이 코드는 UI 요소가 연결된 메서드 NSWindow 를 사용하여 TrackEventsMatching 이벤트 및 LeftMouseDragged 이벤트를 가로챌 수 LeftMouseUp 있습니다. LeftMouseUp 이벤트의 경우 UI 요소는 정상적으로 응답합니다. LeftMouseDragged 이벤트의 경우 이벤트는 화면에서 창을 이동하는 '의 PerformWindowDrag 메서드로 전달NSWindow됩니다.

클래스의 PerformWindowDrag 메서드를 NSWindow 호출하면 다음과 같은 이점이 제공됩니다.

  • 앱이 중단된 경우에도(예: 딥 루프를 처리할 때) 창이 이동할 수 있습니다.
  • 공간 전환은 예상대로 작동합니다.
  • 공백 막대는 정상적으로 표시됩니다.
  • 창 맞춤 및 맞춤은 정상적으로 작동합니다.

최신 컨테이너 뷰 컨트롤 사용

macOS Sierra는 이전 버전의 OS에서 사용할 수 있는 기존 컨테이너 뷰 컨트롤을 다양하게 개선했습니다.

테이블 뷰 향상된 기능

개발자는 항상 새 NSView 기반 버전의 컨테이너 뷰 컨트롤(예: NSTableView)을 사용해야 합니다. 예시:

using System;
using System.Collections.Generic;
using Foundation;
using AppKit;

namespace MacModern
{
    public class ContentsTableDelegate : NSTableViewDelegate
    {
        #region Constructors
        public ContentsTableDelegate ()
        {
        }
        #endregion

        #region Override Methods
        public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
        {
            // Build new view
            var view = new NSView ();
            ...

            // Return the view representing the item to display
            return view;
        }
        #endregion
    }
}

이렇게 하면 사용자 지정 테이블 행 작업을 테이블의 지정된 행에 연결할 수 있습니다(예: 행을 삭제하려면 오른쪽으로 살짝 밀기). 이 동작을 사용하려면 다음의 메서드를 RowActions 재정의합니다 NSTableViewDelegate.

using System;
using System.Collections.Generic;
using Foundation;
using AppKit;

namespace MacModern
{
    public class ContentsTableDelegate : NSTableViewDelegate
    {
        #region Constructors
        public ContentsTableDelegate ()
        {
        }
        #endregion

        #region Override Methods
        public override NSView GetViewForItem (NSTableView tableView, NSTableColumn tableColumn, nint row)
        {
            // Build new view
            var view = new NSView ();
            ...

            // Return the view representing the item to display
            return view;
        }

        public override NSTableViewRowAction [] RowActions (NSTableView tableView, nint row, NSTableRowActionEdge edge)
        {
            // Take action based on the edge
            if (edge == NSTableRowActionEdge.Trailing) {
                // Create row actions
                var editAction = NSTableViewRowAction.FromStyle (NSTableViewRowActionStyle.Regular, "Edit", (action, rowNum) => {
                    // Handle row being edited
                    ...
                });

                var deleteAction = NSTableViewRowAction.FromStyle (NSTableViewRowActionStyle.Destructive, "Delete", (action, rowNum) => {
                    // Handle row being deleted
                    ...
                });

                // Return actions
                return new [] { editAction, deleteAction };
            } else {
                // No matching actions
                return null;
            }
        }
        #endregion
    }
}

정적 NSTableViewRowAction.FromStyle 은 다음 스타일의 새 테이블 행 동작을 만드는 데 사용됩니다.

  • Regular - 행의 내용 편집과 같은 비폭력적인 표준 작업을 수행합니다.
  • Destructive - 테이블에서 행을 삭제하는 등의 파괴적인 작업을 수행합니다. 이러한 작업은 빨간색 배경으로 렌더링됩니다.

스크롤 보기 향상

스크롤 보기(NSScrollView)를 직접 사용하거나 다른 컨트롤(예: NSTableView)의 일부로 사용하는 경우 스크롤 보기의 콘텐츠는 최신 모양 및 보기를 사용하여 Xamarin.Mac 앱의 제목 및 도구 모음 영역 아래로 슬라이드할 수 있습니다.

따라서 스크롤 보기 콘텐츠 영역의 첫 번째 항목은 제목 및 도구 모음 영역에 의해 부분적으로 가려질 수 있습니다.

이 문제를 해결하기 위해 Apple은 클래스에 두 개의 새 속성을 추가했습니다 NSScrollView .

  • ContentInsets - 개발자가 스크롤 보기의 맨 위에 적용할 오프셋을 정의하는 개체를 제공할 NSEdgeInsets 수 있습니다.
  • AutomaticallyAdjustsContentInsets- 스크롤 보기가 개발자를 ContentInsets 위해 자동으로 처리되는 경우 true

개발자는 ContentInsets 다음과 같은 액세서리를 포함할 수 있도록 스크롤 보기의 시작을 조정할 수 있습니다.

  • 메일 앱에 표시된 것과 같은 정렬 표시기입니다.
  • 검색 필드입니다.
  • 새로 고침 또는 업데이트 단추입니다.

최신 앱의 자동 레이아웃 및 지역화

Apple은 개발자가 국제화된 macOS 앱을 쉽게 만들 수 있도록 Xcode에 여러 기술을 포함했습니다. 이제 Xcode를 사용하면 개발자가 스토리보드 파일의 사용자 인터페이스 디자인과 사용자 연결 텍스트를 분리할 수 있으며, UI가 변경되면 이 분리를 기본 수 있는 도구를 제공합니다.

자세한 내용은 Apple의 국제화 및 지역화 가이드를 참조하세요.

기본 국제화 구현

기본 국제화를 구현하여 개발자는 앱의 UI를 나타내고 모든 사용자 연결 문자열을 구분하는 단일 Storyboard 파일을 제공할 수 있습니다.

개발자가 앱의 사용자 인터페이스를 정의하는 초기 Storyboard 파일(또는 파일)을 만들 때 기본 국제화(개발자가 말하는 언어)로 빌드됩니다.

다음으로, 개발자는 여러 언어로 번역할 수 있는 지역화 및 기본 국제화 문자열(Storyboard UI 디자인)을 내보낼 수 있습니다.

나중에 이러한 지역화를 가져올 수 있으며 Xcode는 Storyboard에 대한 언어별 문자열 파일을 생성합니다.

지역화를 지원하기 위한 자동 레이아웃 구현

지역화된 버전의 문자열 값은 크기 및/또는 읽기 방향이 크게 다를 수 있으므로 개발자는 자동 레이아웃을 사용하여 스토리보드 파일에서 앱의 사용자 인터페이스를 배치하고 크기를 조정해야 합니다.

Apple은 다음을 수행하는 것이 좋습니다.

  • 고정 너비 제약 조건 제거 - 모든 텍스트 기반 뷰의 크기를 콘텐츠에 따라 조정할 수 있어야 합니다. 고정 너비 보기는 특정 언어로 콘텐츠를 자릅니다.
  • 내장 콘텐츠 크기 사용 - 기본적으로 텍스트 기반 보기는 콘텐츠에 맞게 자동 크기가 조정됩니다. 크기가 올바르지 않은 텍스트 기반 보기의 경우 Xcode의 인터페이스 작성기에서 선택한 다음, 콘텐츠에 맞게 크기 편집>을 선택합니다.
  • 선행 및 후행 특성 적용 - 텍스트의 방향은 사용자의 언어에 따라 변경될 수 있으므로 기존 Right 특성과 Left 특성이 아닌 새 Leading 제약 Trailing 조건 특성을 사용합니다. Leading 언어 Trailing 방향에 따라 자동으로 조정됩니다.
  • 인접 뷰 에 보기 고정 - 선택한 언어에 따라 뷰의 위치가 변경될 때 뷰의 위치를 변경하고 크기를 조정할 수 있습니다.
  • Windows 최소 및/또는 최대 크기 설정 안 함 - 선택한 언어가 콘텐츠 영역의 크기를 조정함에 따라 Windows에서 크기를 변경할 수 있도록 허용합니다.
  • 지속적으로 테스트 레이아웃 변경 - 앱에서 개발하는 동안 다른 언어로 지속적으로 테스트해야 합니다. 자세한 내용은 Apple의 국제화된 앱 테스트 설명서를 참조하세요.
  • NSStackViews를 사용하여 보기를 함께 - NSStackViews 고정하면 콘텐츠가 예측 가능한 방식으로 이동하고 증가할 수 있으며 선택한 언어에 따라 콘텐츠 크기가 변경됩니다.

Xcode의 인터페이스 작성기에서 지역화

Apple은 개발자가 지역화를 지원하기 위해 앱의 UI를 디자인하거나 편집할 때 사용할 수 있는 Xcode의 Interface Builder에 몇 가지 기능을 제공했습니다. 특성 검사기의 텍스트 방향 섹션을 사용하면 개발자가 선택한 텍스트 기반 보기(예: NSTextField)에서 방향을 사용하고 업데이트하는 방법에 대한 힌트를 제공할 수 있습니다.

The Text Direction options

텍스트 방향에는 세 가지 가능한 값이 있습니다.

  • 자연 - 레이아웃은 컨트롤에 할당된 문자열을 기반으로 합니다.
  • 왼쪽에서 오른쪽으로 - 레이아웃은 항상 왼쪽에서 오른쪽으로 강제 적용됩니다.
  • 오른쪽에서 왼쪽으로 - 레이아웃은 항상 오른쪽에서 왼쪽으로 강제됩니다.

레이아웃에는 두 가지 가능한 값이 있습니다.

  • 왼쪽에서 오른쪽으로 - 레이아웃은 항상 왼쪽에서 오른쪽입니다.
  • 오른쪽에서 왼쪽으로 - 레이아웃은 항상 오른쪽에서 왼쪽으로.

일반적으로 특정 맞춤이 필요하지 않은 경우 변경하면 안 됩니다.

미러 속성은 특정 컨트롤 속성(예: 셀 이미지 위치)을 대칭 이동하도록 시스템에 지시합니다. 세 가지 값은 다음과 같습니다.

  • 자동으로 - 선택한 언어의 방향에 따라 위치가 자동으로 변경됩니다.
  • 오른쪽에서 왼쪽으로 인터페이스 - 위치는 오른쪽에서 왼쪽 기반 언어로만 변경됩니다.
  • 결코 - 위치는 변경되지 않습니다.

개발자가 텍스트 기반 보기의 콘텐츠에 대한 가운데, 양쪽 맞춤 또는 전체 맞춤을 지정한 경우 선택한 언어에 따라 대칭 이동되지 않습니다.

macOS Sierra 이전에는 코드에서 만든 컨트롤이 자동으로 미러 않습니다. 개발자는 다음과 같은 코드를 사용하여 미러 처리해야 했습니다.

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

    // Setting a button's mirroring based on the layout direction
    var button = new NSButton ();
    if (button.UserInterfaceLayoutDirection == NSUserInterfaceLayoutDirection.LeftToRight) {
        button.Alignment = NSTextAlignment.Right;
        button.ImagePosition = NSCellImagePosition.ImageLeft;
    } else {
        button.Alignment = NSTextAlignment.Left;
        button.ImagePosition = NSCellImagePosition.ImageRight;
    }
}

컨트롤에 Alignment 따라 UserInterfaceLayoutDirection 설정되는 위치입니다ImagePosition.

macOS Sierra는 여러 매개 변수(예: 제목, 이미지 및 작업)를 사용하고 자동으로 올바르게 미러 수 있는 몇 가지 새로운 편리한 생성자(정적 CreateButton 메서드를 통해)를 추가합니다. 예시:

var button2 = NSButton.CreateButton (myTitle, myImage, () => {
    // Take action when the button is pressed
    ...
});

시스템 모양 사용

최신 macOS 앱은 이미지 만들기, 편집 또는 프레젠테이션 앱에 적합한 새로운 다크 인터페이스 모양을 채택할 수 있습니다.

An example of a dark Mac Window UI

이 작업은 창이 표시되기 전에 한 줄의 코드를 추가하여 수행할 수 있습니다. 예시:

using System;
using AppKit;
using Foundation;

namespace MacModern
{
    public partial class ViewController : NSViewController
    {
        ...

        #region Override Methods
        public override void ViewWillAppear ()
        {
            base.ViewWillAppear ();

            // Apply the Dark Interface Appearance
            View.Window.Appearance = NSAppearance.GetAppearance (NSAppearance.NameVibrantDark);

            ...
        }
        #endregion
    }
}

클래스의 NSAppearance 정적 GetAppearance 메서드는 시스템에서 명명된 모양을 가져오는 데 사용됩니다(이 경우NSAppearance.NameVibrantDark).

Apple에는 시스템 모양을 사용하기 위한 다음과 같은 제안이 있습니다.

  • 하드 코딩된 값(예: LabelColorSelectedControlColor)보다 명명된 색을 선호합니다.
  • 가능한 경우 시스템 표준 컨트롤 스타일을 사용합니다.

시스템 모양을 사용하는 macOS 앱은 시스템 기본 설정 앱에서 접근성 기능을 사용하도록 설정한 사용자에 대해 자동으로 올바르게 작동합니다. 따라서 Apple은 개발자가 macOS 앱에서 항상 시스템 모양을 사용해야 한다고 제안합니다.

Storyboard를 사용하여 UI 디자인

스토리보드를 사용하면 개발자가 앱의 사용자 인터페이스를 구성하는 개별 요소를 디자인할 뿐만 아니라 지정된 요소의 UI 흐름과 계층 구조를 시각화하고 디자인할 수 있습니다.

컨트롤러를 사용하면 개발자가 요소를 컴퍼지션 단위로 수집하고 Segues 추상화하고 뷰 계층 전체에서 이동하는 데 필요한 일반적인 "붙이기 코드"를 제거할 수 있습니다.

Editing the UI in Xcode's Interface Builder

자세한 내용은 Storyboards 소개 설명서를 참조하세요.

스토리보드에 정의된 지정된 장면에 보기 계층 구조의 이전 장면의 데이터가 필요한 경우가 많습니다. Apple에는 장면 간에 정보를 전달하기 위한 다음과 같은 제안이 있습니다.

  • 데이터 종속성은 항상 계층 구조를 통해 아래로 계단식으로 계단식으로 연결되어야 합니다.
  • UI 유연성을 제한하므로 UI 구조적 의존성을 하드 코딩하지 마십시오.
  • C# 인터페이스를 사용하여 제네릭 데이터 의존성을 제공합니다.

Segue의 원본 역할을 하는 뷰 컨트롤러는 대상 뷰 컨트롤러를 표시하기 위해 Segue를 실행하기 전에 메서드를 재정 PrepareForSegue 의하고 필요한 모든 초기화(예: 데이터 전달)를 수행할 수 있습니다. 예시:

public override void PrepareForSegue (NSStoryboardSegue segue, NSObject sender)
{
    base.PrepareForSegue (segue, sender);

    // Take action based on Segue ID
    switch (segue.Identifier) {
    case "MyNamedSegue":
        // Prepare for the segue to happen
        ...
        break;
    }
}

자세한 내용은 Segues 설명서를 참조하세요.

작업 전파

macOS 앱의 디자인에 따라 UI 컨트롤에 대한 작업에 가장 적합한 처리기가 UI 계층 구조의 다른 위치에 있을 수 있는 경우가 있을 수 있습니다. 이는 일반적으로 앱의 나머지 UI와는 별도로 자체 장면에 있는 메뉴 및 메뉴 항목에 해당합니다.

이 상황을 처리하기 위해 개발자는 사용자 지정 작업을 만들고 작업을 응답자 체인에 전달할 수 있습니다. 자세한 내용은 사용자 지정 창 작업 작업 설명서를 참조하세요.

최신 Mac 기능

Apple은 개발자가 다음과 같은 Mac 플랫폼을 최대한 활용할 수 있도록 macOS Sierra에 여러 사용자 연결 기능을 포함했습니다.

  • NSUserActivity - 이를 통해 앱은 사용자가 현재 참여하고 있는 활동을 설명할 수 있습니다. NSUserActivity 는 처음에 HandOff를 지원하기 위해 만들어졌으며, 여기서 사용자의 디바이스 중 하나에서 활동을 시작하여 다른 디바이스에서 계속 사용할 수 있습니다. NSUserActivity는 iOS에서와 동일하게 작동하므로 자세한 내용은 핸드오프 iOS 소개 설명서를 참조하세요.
  • Mac 의 Siri - Siri는 현재 작업(NSUserActivity)을 사용하여 사용자가 발급할 수 있는 명령에 컨텍스트를 제공합니다.
  • 상태 복원 - 사용자가 macOS에서 앱을 종료한 다음 나중에 다시 실행하면 앱이 자동으로 이전 상태로 반환됩니다. 개발자는 사용자 인터페이스가 사용자에게 표시되기 전에 상태 복원 API를 사용하여 일시적인 UI 상태를 인코딩하고 복원할 수 있습니다. 앱이 기반이 NSDocument 되면 상태 복원이 자동으로 처리됩니다. 비기NSDocument반 앱에 대해 상태 복원을 사용하도록 설정하려면 클래스trueNSWindowRestorable .로 설정합니다.
  • 클라우드 의 문서 - macOS Sierra 이전에는 앱이 사용자의 iCloud 드라이브에서 문서 작업을 명시적으로 옵트인해야 했습니다. macOS Sierra에서 사용자의 데스크톱문서 폴더는 시스템에서 iCloud 드라이브와 자동으로 동기화될 수 있습니다. 따라서 문서의 로컬 복사본을 삭제하여 사용자 컴퓨터의 공간을 확보할 수 있습니다. NSDocument 기반 앱은 이 변경을 자동으로 처리합니다. 다른 모든 앱 유형은 문서의 읽기 및 쓰기를 NSFileCoordinator 동기화하는 데 사용해야 합니다.

요약

이 문서에서는 개발자가 Xamarin.Mac에서 최신 macOS 앱을 빌드하는 데 사용할 수 있는 몇 가지 팁, 기능 및 기술을 설명했습니다.