Поделиться через


Создание современных приложений macOS

В этой статье рассматриваются несколько советов, функций и методов, которые разработчик может использовать для создания современного приложения macOS в Xamarin.Mac.

Создание современных взглядов с помощью современных представлений

Современный внешний вид будет включать современный внешний вид окна и панели инструментов, например пример приложения, показанного ниже:

Пример современного пользовательского интерфейса приложения Mac

Включение представлений содержимого полного размера

Чтобы добиться этого в приложении 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, выбрав окно и проверив представление содержимого полного размера:

Редактирование основной раскадровки в построителе интерфейсов Xcode

При использовании представления содержимого полного размера разработчик может потребовать смещения содержимого под заголовком и областями панели инструментов, чтобы определенное содержимое (например, метки) не скользит под ними.

Чтобы усложнить эту проблему, области заголовка и панели инструментов могут иметь динамическую высоту на основе действия, которое пользователь в настоящее время выполняет, версия macOS, установленная пользователем и (или) оборудование Mac, на котором работает приложение.

В результате просто жесткое кодирование смещения при создании пользовательского интерфейса не будет работать. Разработчику потребуется использовать динамический подход.

Apple включила свойство Key-Value Observable ContentLayoutRect класса, чтобы получить текущую область содержимого NSWindow в коде. Разработчик может использовать это значение для ручного размещения необходимых элементов при изменении области содержимого.

Лучше всего использовать классы автоматического макета и размера для размещения элементов пользовательского интерфейса в коде или построителе интерфейсов.

Код, как и в следующем примере, можно использовать для размещения элементов пользовательского интерфейса с помощью классов AutoLayout и Size в контроллере представления приложения:

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 включает стандартную строку заголовка при выполнении вдоль верхней границы окна. Если окно также включает панель инструментов, она будет отображаться в этой области заголовка:

Стандартная панель инструментов Mac

При использовании упрощенной панели инструментов область заголовка исчезает, а панель инструментов перемещается в положение заголовка, в строке с кнопками "Закрыть окно", "Свернуть" и "Развернуть":

Упрощенная панель инструментов Mac

Оптимизированная панель инструментов включена, переопределяя ViewWillAppear метод и NSViewController делая его похожим следующим образом:

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

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

Этот эффект обычно используется для приложений Shoebox (одно окно), таких как карты, календарь, заметки и системные настройки.

Использование контроллеров представления аксессуаров

В зависимости от дизайна приложения разработчик может также дополнить область заголовка панелью заголовков с контроллером представления аксессуара, который отображается прямо под областью заголовка или панели инструментов, чтобы предоставить пользователю контекстные элементы управления на основе действия, в который они в настоящее время участвуют:

Пример контроллера представления аксессуаров

Контроллер доступа будет автоматически размыт и изменен системой без вмешательства разработчика.

Чтобы добавить контроллер представления аксессуаров, сделайте следующее:

  1. В обозревателе решений дважды щелкните файл Main.storyboard, чтобы открыть его для редактирования.

  2. Перетащите контроллер пользовательского представления в иерархию окна:

    Добавление нового пользовательского контроллера представления

  3. Макет пользовательского интерфейса представления аксессуара:

    Проектирование нового представления

  4. Предоставление представления аксессуара в качестве выхода и любых других действий или точек для пользовательского интерфейса:

    Добавление требуемого OUtlet

  5. Сохраните изменения.

  6. Вернитесь к Visual Studio для Mac для синхронизации изменений.

Измените 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 свойства устарели и NSLayoutAttribute Right должны быть заменены на Leading и Trailing.

Использование вкладки Windows

Кроме того, система macOS может добавлять контроллеры представления аксессуаров в окно приложения. Например, чтобы создать вкладки Windows, где несколько windows приложения объединяются в одно виртуальное окно:

Пример табуляции окна Mac

Как правило, разработчику потребуется предпринять ограниченные действия с помощью табуляции Windows в приложениях Xamarin.Mac, система будет обрабатывать их автоматически следующим образом:

  • Windows автоматически будет добавлена на вкладку при вызове OrderFront метода.
  • Windows автоматически будет отключен при вызове OrderOut метода.
  • В коде все окна табуляции по-прежнему считаются "видимыми", однако все внешние вкладки скрыты системой с помощью CoreGraphics.
  • TabbingIdentifier Используйте свойство для группировки NSWindow Windows на вкладки.
  • Если это NSDocument приложение на основе, некоторые из этих функций будут включены автоматически (например, кнопка "плюс", добавляемая на панель вкладок) без каких-либо действий разработчика.
  • NSDocument Не зависящие от приложений могут включить кнопку "плюс" в группе вкладок, чтобы добавить новый документ, переопределив GetNewWindowForTab метод объекта NSWindowsController.

Объединение всех частей приложения, AppDelegate которое хотело использовать системные вкладки Windows, может выглядеть следующим образом:

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, что приложение предпочитает использовать вкладки и предоставляет строку, которая позволяет приложению группироваться на вкладки:

// 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. Базовая анимация оптимизирована для использования gpu (единица обработки графики), доступной в современном оборудовании macOS, а не для выполнения графических операций на ЦП, что может замедлить работу компьютера.

С CALayerпомощью основной анимации можно использовать такие задачи, как быстрая и динамная прокрутка и анимация. Пользовательский интерфейс приложения должен состоять из нескольких вложенных представлений и слоев, чтобы полностью воспользоваться преимуществами основной анимации.

Объект CALayer предоставляет несколько свойств, позволяющих разработчику управлять тем, что отображается на экране пользователю, например:

  • Content — может быть или NSImage CGImage предоставляет содержимое слоя.
  • BackgroundColor — задает цвет фона слоя в виде CGColor
  • BorderWidth — задает ширину границы.
  • BorderColor — задает цвет границы.

Чтобы использовать базовую графику в пользовательском интерфейсе приложения, она должна использовать представления с поддержкой слоев , которые Apple предлагает разработчику всегда включить в представлении содержимого окна. Таким образом, все дочерние представления автоматически наследуют резервное копирование слоев.

Кроме того, Apple предлагает использовать представления с поддержкой слоев вместо добавления нового CALayer в подложку, так как система будет автоматически обрабатывать несколько необходимых параметров (например, необходимых для дисплея Сетчатки).

Резервное копирование слоев можно включить, задав WantsLayer true NSView для конструктора интерфейсов Xcode или внутри конструктора интерфейсов Xcode в инспекторе эффектов представления, проверив основной уровень анимации:

Инспектор эффектов представления

Перерисовка представлений с помощью слоев

Еще одним важным шагом при использовании представлений с поддержкой слоев в приложении Xamarin.Mac является установка LayerContentsRedrawPolicy NSView значения OnSetNeedsDisplay в элементе NSViewController. Например:

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

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

Если разработчик не задает это свойство, представление будет перезавернуто всякий раз, когда изменяется источник кадра, что не нужно по соображениям производительности. Однако при использовании этого свойства OnSetNeedsDisplay разработчику придется вручную задать NeedsDisplay для true принудительного перерисовки содержимого.

Если представление помечается как грязное, система проверяет WantsUpdateLayer свойство View. Если метод возвращается true , UpdateLayer метод вызывается, а другой DrawRect метод представления вызывается для обновления содержимого представления.

Apple предлагает следующие предложения по обновлению содержимого представления при необходимости:

  • Apple предпочитает использовать UpdateLater по DrawRect мере возможности, так как она обеспечивает значительный рост производительности.
  • Используйте то же самое layer.Contents для элементов пользовательского интерфейса, которые выглядят аналогично.
  • Apple также предпочитает разработчику создавать свой пользовательский интерфейс с помощью стандартных представлений, таких как NSTextField, когда это возможно.

Чтобы использовать 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
    }
}

Использование современного перетаскивания

Чтобы представить современный интерфейс перетаскивания для пользователя, разработчик должен применить перетаскивание в операциях перетаскивания приложения. Перетащите 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
    }
}

Эффект стека был достигнут путем отправки каждого элемента, перетаскиваемого в BeginDraggingSession метод NSView отдельного элемента в массиве.

При работе с классом NSTableView или NSOutlineViewиспользуйте PastboardWriterForRow метод NSTableViewDataSource класса, чтобы запустить операцию перетаскивания:

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

Это позволяет разработчику предоставлять отдельный NSDraggingItem элемент в таблице, который перетаскивается, а не старый метод WriteRowsWith , который записывает все строки как одну группу в вставку.

При работе NSCollectionViewsс ним снова используйте PasteboardWriterForItemAt метод, а не WriteItemsAt метод при начале перетаскивания.

Разработчик всегда должен избегать размещения больших файлов на вставке. Новые для macOS Sierra, File Promises позволяют разработчику размещать ссылки на заданные файлы на вставке, которые позже будут выполнены, когда пользователь завершит операцию drop с помощью новых NSFilePromiseProvider и NSFilePromiseReceiver классов.

Использование современного отслеживания событий

Для элемента пользовательского интерфейса (например NSButton, который был добавлен в область заголовка или панели инструментов), пользователь должен иметь возможность щелкнуть элемент и запустить событие как обычное (например, отображение всплывающего окна). Однако, так как элемент также находится в области заголовка или панели инструментов, пользователь должен иметь возможность щелкнуть и перетащить элемент для перемещения окна.

Чтобы выполнить это в коде, создайте пользовательский класс для элемента (например 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);
    }
}

Этот код использует TrackEventsMatching метод присоединенного элемента пользовательского NSWindow интерфейса для перехвата LeftMouseUp событий и LeftMouseDragged событий. Для события элемент пользовательского LeftMouseUp интерфейса отвечает как обычно. LeftMouseDragged Для события событие передается методу NSWindowPerformWindowDrag метода перемещения окна на экране.

PerformWindowDrag Вызов метода NSWindow класса обеспечивает следующие преимущества:

  • Он позволяет перемещать окно, даже если приложение зависается (например, при обработке глубокого цикла).
  • Переключение пространства будет работать должным образом.
  • Панель пробелов будет отображаться как обычная.
  • Привязка окна и выравнивание работают нормально.

Использование современных элементов управления представления контейнеров

macOS Sierra предоставляет множество современных улучшений существующих элементов управления представлением контейнеров, доступных в предыдущей версии ОС.

Улучшения представления таблиц

Разработчик всегда должен использовать новую 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 — выполняет деструктивное действие, например удаление строки из таблицы. Эти действия будут отображаться красным фоном.

Усовершенствования представления прокрутки

При использовании представления прокрутки () напрямую или в составе другого элемента управления (напримерNSTableView, содержимое представленияNSScrollView прокрутки может скользить в области заголовка и панели инструментов в приложении Xamarin.Mac с помощью современного внешнего вида и представлений).

В результате первый элемент в области содержимого представления прокрутки может быть частично скрыт областью заголовка и панели инструментов.

Чтобы устранить эту проблему, Apple добавила в класс два новых свойства NSScrollView :

  • ContentInsets — Позволяет разработчику предоставить объект, определяющий NSEdgeInsets смещение, которое будет применено к верхней части представления прокрутки.
  • AutomaticallyAdjustsContentInsets — Если true представление прокрутки будет автоматически обрабатываться ContentInsets для разработчика.

С помощью ContentInsets разработчика можно настроить начало представления прокрутки, чтобы разрешить включение таких аксессуаров, как:

  • Индикатор сортировки, как в приложении "Почта".
  • Поле поиска.
  • Кнопка "Обновить" или "Обновить".

Автоматическое макет и локализация в современных приложениях

Apple включила несколько технологий в Xcode, которые позволяют разработчику легко создавать международное приложение macOS. Теперь Xcode позволяет разработчику отделять текст, доступный для пользователя, от дизайна пользовательского интерфейса приложения в файлах раскадровки и предоставляет средства для поддержания этого разделения при изменении пользовательского интерфейса.

Дополнительные сведения см. в руководстве по интернационализации и локализации Apple.

Реализация базовой интернационализации

Реализуя базовую интернационализацию, разработчик может предоставить один раскадровочный файл, чтобы представить пользовательский интерфейс приложения и разделить все строки, доступные для пользователя.

Когда разработчик создает исходный раскадровки (или файлы), определяющий пользовательский интерфейс приложения, он будет создан в базовой интернационализации (язык, на котором говорит разработчик).

Затем разработчик может экспортировать локализации и строки базовой интернационализации (в конструкторе пользовательского интерфейса раскадровки), которые можно перевести на несколько языков.

Позже эти локализации можно импортировать, и Xcode создаст файлы строк для раскадровки для конкретного языка.

Реализация автоматического макета для поддержки локализации

Так как локализованные версии строковых значений могут иметь значительно разные размеры и /или направление чтения, разработчик должен использовать авто макет для размещения и размера пользовательского интерфейса приложения в раскадровки-файле.

Apple предлагает сделать следующее:

  • Удалить ограничения фиксированной ширины. Все текстовые представления должны быть разрешены для изменения размера на основе их содержимого. Представление фиксированной ширины может обрезать содержимое на определенных языках.
  • Используйте встроенные размеры контента. По умолчанию текстовые представления автоматически будут размерировать их содержимое. В текстовом представлении, которое неправильно изменяет размер, выберите их в построителе интерфейсов Xcode, а затем выберите "Изменить>размер в соответствии с содержимым".
  • Применение начальных и конечных атрибутов . Так как направление текста может меняться на основе языка пользователя, используйте новые Leading и Trailing ограничивающие атрибуты, а не существующие Right и Left атрибуты. Leading и Trailing будет автоматически настраиваться на основе направления языков.
  • Закрепление представлений к смежным представлениям . Это позволяет представлениям изменять положение и изменять размер в виде представлений вокруг них в ответ на выбранный язык.
  • Не устанавливайте минимальный и /или максимальный размер Windows. Разрешить Windows изменять размер, так как выбранный язык изменяет размер областей содержимого.
  • Изменения макета теста постоянно изменяются. Во время разработки в приложении следует постоянно тестировать на разных языках. Дополнительные сведения см. в документации по тестированию приложений Apple internationalized .
  • Использование NSStackViews для объединения представлений - NSStackViews позволяет их содержимому перемещаться и увеличиваться прогнозируемыми способами и размером изменения содержимого на основе выбранного языка.

Локализация в построителе интерфейсов Xcode

Apple предоставила несколько функций в конструкторе интерфейсов Xcode, которые разработчик может использовать при проектировании или редактировании пользовательского интерфейса приложения для поддержки локализации. Раздел "Направление текста" инспектора атрибутов позволяет разработчику предоставлять указания о том, как следует использовать направление и обновляться в представлении на основе текста (напримерNSTextField: ):

Параметры направления текста

Для направления текста можно использовать три возможных значения:

  • Natural — макет основан на строке, назначенной элементу управления.
  • Слева направо — макет всегда вынужден слева направо.
  • Справа налево — макет всегда вынужден справа налево .

Для макета можно использовать два возможных значения:

  • Слева направо — макет всегда слева направо.
  • Справа налево — макет всегда справа налево .

Как правило, их не следует изменять, если не требуется определенное выравнивание.

Свойство Mirror сообщает системе перевернуть определенные свойства элемента управления (например, положение изображения ячейки). У него есть три возможных значения:

  • Автоматически — позиция будет автоматически меняться в зависимости от направления выбранного языка.
  • В интерфейсе справа налево — позиция будет изменена только вправо на языки налево.
  • Никогда - позиция никогда не изменится.

Если разработчик указал Центр, оправдание или полное выравнивание содержимого текстового представления, они никогда не будут перевернуты на основе выбранного языка.

До 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 Где устанавливается и ImagePosition устанавливается на UserInterfaceLayoutDirection основе элемента управления.

macOS Sierra добавляет несколько новых удобных конструкторов (через статический CreateButton метод), которые принимают несколько параметров (например, Title, Image и Action) и автоматически зеркально отражаются. Например:

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

Использование системных внешних типов

Современные приложения macOS могут использовать новый внешний вид темного интерфейса, который хорошо подходит для создания изображений, редактирования или презентации приложений:

Пример темного пользовательского интерфейса Окна Mac

Это можно сделать, добавив одну строку кода перед представлением окна. Например:

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

Статический GetAppearance метод NSAppearance класса используется для получения именованного внешнего вида из системы (в данном случае NSAppearance.NameVibrantDark).

Apple предлагает следующие варианты использования системных внешних элементов:

  • Предпочитайте именованные цвета для жестко закодированных значений (например LabelColor , и SelectedControlColor).
  • По возможности используйте стандартный стиль управления системой.

Приложение macOS, использующее системные внешние виды, автоматически работает для пользователей, которые включили специальные возможности из приложения "Системные настройки". В результате Apple предлагает разработчику всегда использовать системные внешние возможности в своих приложениях macOS.

Проектирование пользовательских интерфейсов с помощью раскадровки

Раскадровки позволяют разработчику создавать не только отдельные элементы, составляющие пользовательский интерфейс приложения, но и визуализировать поток пользовательского интерфейса и иерархию заданных элементов.

Контроллеры позволяют разработчику собирать элементы в единицу композиции и абстрактных segues и удалять типичный "клей код", необходимый для перемещения по иерархии представлений:

Изменение пользовательского интерфейса в построителе интерфейсов Xcode

Дополнительные сведения см. в документации по раскадровкам.

Существует множество случаев, когда определенная сцена, определенная в раскадровке, потребует данных из предыдущей сцены в иерархии представлений. Apple предлагает следующие предложения для передачи информации между сценами:

  • Зависимости данных всегда должны каскадироваться вниз по иерархии.
  • Избегайте жестких зависимостей пользовательского интерфейса, так как это ограничивает гибкость пользовательского интерфейса.
  • Используйте интерфейсы C# для предоставления универсальных зависимостей данных.

Контроллер представления, который выступает в качестве источника segue, может переопределить PrepareForSegue метод и выполнить все необходимые инициализации (например, передачи данных) перед выполнением segue для отображения целевого контроллера представления. Например:

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 может возникнуть время, когда лучший обработчик действия элемента управления пользовательского интерфейса может находиться в другом месте иерархии пользовательского интерфейса. Обычно это верно для элементов меню и меню, которые живут в собственной сцене, отдельно от остального пользовательского интерфейса приложения.

Чтобы справиться с этой ситуацией, разработчик может создать настраиваемое действие и передать действие в цепочку отклика. Дополнительные сведения см . в документации по работе с пользовательскими действиями окна.

Современные функции Mac

Apple включила в macOS Sierra несколько пользовательских функций, которые позволяют разработчику использовать большую часть платформы Mac, например:

  • NSUserActivity — это позволяет приложению описать действие, к которому в настоящее время участвует пользователь. NSUserActivity изначально был создан для поддержки HandOff, где действие, запущенное на одном из устройств пользователя, может быть выбрано и продолжено на другом устройстве. NSUserActivity работает так же в macOS, как и в iOS, поэтому дополнительные сведения см. в нашей документации по handoff iOS.
  • Siri в Mac — Siri использует текущее действие (NSUserActivity) для предоставления контекста командам, которые пользователь может выдавать.
  • Восстановление состояния. Когда пользователь выходит из приложения в macOS, а затем повторно запускает его, приложение автоматически будет возвращено в предыдущее состояние. Разработчик может использовать API восстановления состояния для кодирования и восстановления временных состояний пользовательского интерфейса перед отображением пользовательского интерфейса пользователю. Если приложение NSDocument основано, восстановление состояния обрабатывается автоматически. Чтобы включить восстановление состояния для приложений, неNSDocument основанных на основе, задайте Restorable NSWindow для класса trueзначение .
  • Документы в облаке — до macOS Sierra приложение пришлось явно согласиться на работу с документами в iCloud Drive пользователя. В macOS Sierra папки рабочего стола и документов пользователя могут быть синхронизированы с iCloud Drive автоматически системой. В результате локальные копии документов могут быть удалены, чтобы освободить место на компьютере пользователя. NSDocument приложения на основе автоматически обрабатывают это изменение. Все остальные типы приложений должны использовать NSFileCoordinator для синхронизации чтения и записи документов.

Итоги

В этой статье рассматриваются несколько советов, функций и методов, которые разработчик может использовать для создания современного приложения macOS в Xamarin.Mac.