Udostępnij za pośrednictwem


Tworzenie nowoczesnych aplikacji systemu macOS

W tym artykule opisano kilka wskazówek, funkcji i technik, których deweloper może użyć do utworzenia nowoczesnej aplikacji systemu macOS na platformie Xamarin.Mac.

Tworzenie nowoczesnego wyglądu z nowoczesnymi widokami

Nowoczesny wygląd będzie zawierać nowoczesny wygląd okna i paska narzędzi, taki jak przykładowa aplikacja pokazana poniżej:

Przykład nowoczesnego interfejsu użytkownika aplikacji dla komputerów Mac

Włączanie widoków zawartości o pełnym rozmiarze

Aby to osiągnąć w aplikacji platformy Xamarin.Mac, deweloper będzie chciał użyć widoku zawartości o pełnym rozmiarze, co oznacza, że zawartość rozciąga się w obszarze Narzędzia i pasek tytułu i zostanie automatycznie rozmyta przez system macOS.

Aby włączyć tę funkcję w kodzie, utwórz klasę niestandardową dla NSWindowController klasy i ustaw ją tak, jakby wyglądała następująco:

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

Tę funkcję można również włączyć w narzędziu Interface Builder programu Xcode, wybierając okno i sprawdzając widok zawartości o pełnym rozmiarze:

Edytowanie głównego scenorysu w narzędziu Xcode Interface Builder

W przypadku korzystania z widoku zawartości o pełnym rozmiarze deweloper może wymagać przesunięcia zawartości pod obszarami tytułu i paska narzędzi, aby określona zawartość (na przykład etykiety) nie przesuwała się pod nimi.

Aby skomplikować ten problem, obszary Tytuł i Pasek narzędzi mogą mieć dynamiczną wysokość na podstawie akcji, na podstawie aktualnie wykonywanej akcji, wersja systemu macOS zainstalowana i/lub sprzęt komputera Mac, na którym działa aplikacja.

W związku z tym po prostu stałe kodowanie przesunięcia podczas układania interfejsu użytkownika nie będzie działać. Deweloper będzie musiał podjąć dynamiczne podejście.

Firma Apple uwzględniła właściwość NSWindow Key-Value ObservableContentLayoutRect klasy, aby uzyskać bieżący obszar zawartości w kodzie. Deweloper może użyć tej wartości, aby ręcznie ustawić wymagane elementy, gdy zmieni się obszar zawartości.

Lepszym rozwiązaniem jest użycie klas automatycznego układu i rozmiaru w celu pozycjonowania elementów interfejsu użytkownika w kodzie lub konstruktorze interfejsu.

Kod podobny do poniższego przykładu może służyć do pozycjonowania elementów interfejsu użytkownika przy użyciu klas AutoLayout i Size w kontrolerze widoku aplikacji:

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

Ten kod tworzy magazyn dla górnego ograniczenia, które zostanie zastosowane do etykiety (ItemTitle), aby upewnić się, że nie spadnie w obszarze Tytuł i Pasek narzędzi:

public NSLayoutConstraint topConstraint { get; set; }

Przesłaniając metodę kontrolera UpdateViewConstraints widoku, deweloper może sprawdzić, czy wymagane ograniczenie zostało już skompilowane i w razie potrzeby utworzyć je.

Jeśli należy skompilować nowe ograniczenie, właściwość okna kontrolki, która musi być ograniczona, ContentLayoutGuide jest uzyskiwana i rzutowana na NSLayoutGuideelement :

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

Dostęp do właściwości NSLayoutGuide TopAnchor obiektu jest dostępny i jeśli jest dostępny, służy do kompilowania nowego ograniczenia z żądaną kwotą przesunięcia, a nowe ograniczenie jest aktywne, aby go zastosować:

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

Włączanie usprawnionych pasków narzędzi

Normalne okno systemu macOS zawiera standardowy pasek tytułu w działach wzdłuż górnej krawędzi okna. Jeśli okno zawiera również pasek narzędzi, zostanie ono wyświetlone w obszarze pasek tytułu:

Standardowy pasek narzędzi dla komputerów Mac

W przypadku korzystania z usprawnionego paska narzędzi obszar tytułu znika, a pasek narzędzi przechodzi w górę do pozycji paska tytułu, w wierszu z przyciskami Zamknij okno, Minimalizuj i Maksymalizuj:

Usprawniony pasek narzędzi dla komputerów Mac

Usprawniony pasek narzędzi jest włączony, przesłaniając metodę ViewWillAppearNSViewController elementu i tworząc go w następujący sposób:

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

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

Ten efekt jest zwykle używany w aplikacjach Shoebox (jednej aplikacji okna), takich jak Mapy, Kalendarz, Notatki i Preferencje systemowe.

Korzystanie z kontrolerów widoku dostępu

W zależności od projektu aplikacji deweloper może również chcieć uzupełnić obszar Pasek tytułu za pomocą kontrolera widoku dostępu, który pojawia się bezpośrednio poniżej obszaru Tytuł/Pasek narzędzi, aby zapewnić kontekstowe kontrolki dla użytkownika na podstawie aktualnie zaangażowanego działania:

Przykładowy kontroler widoku dostępu

Kontroler widoku dostępu zostanie automatycznie rozmyty i zmieniony przez system bez interwencji dewelopera.

Aby dodać kontroler widoku dostępu, wykonaj następujące czynności:

  1. W Eksplorator rozwiązań kliknij Main.storyboard dwukrotnie plik, aby otworzyć go do edycji.

  2. Przeciągnij kontroler widoku niestandardowego do hierarchii okna:

    Dodawanie nowego kontrolera widoku niestandardowego

  3. Układ interfejsu użytkownika widoku dostępu:

    Projektowanie nowego widoku

  4. Uwidocznij widok akcesoriów jako punkt wyjścia i wszelkie inne akcje lub placówki dla swojego interfejsu użytkownika:

    Dodawanie wymaganego elementu OUtlet

  5. Zapisz zmiany.

  6. Wróć do Visual Studio dla komputerów Mac, aby zsynchronizować zmiany.

Zmodyfikuj element NSWindowController i zmień jego wygląd na następujący:

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

Kluczowymi punktami tego kodu jest ustawienie widoku niestandardowego zdefiniowanego w konstruktorze interfejsu i uwidocznione jako gniazdo:

accessoryView.View = AccessoryViewGoBar;

A to LayoutAttribute definiuje , gdzie będzie wyświetlana akcesorium:

accessoryView.LayoutAttribute = NSLayoutAttribute.Bottom;

Ponieważ system macOS jest teraz w pełni zlokalizowany, Left właściwości i RightNSLayoutAttribute zostały wycofane i powinny zostać zastąpione elementami Leading i Trailing.

Korzystanie z systemu Windows z kartami

Ponadto system macOS może dodać kontrolery widoku dostępu do okna aplikacji. Aby na przykład utworzyć okna z kartami, w których kilka okien aplikacji jest scalonych z jednym wirtualnym oknem:

Przykład okna z kartami dla komputerów Mac

Zazwyczaj deweloper musi wykonać ograniczoną akcję przy użyciu systemu Windows z kartami w aplikacjach platformy Xamarin.Mac. System będzie obsługiwał je automatycznie w następujący sposób:

  • System Windows będzie automatycznie kartowany po wywołaniu OrderFront metody.
  • System Windows zostanie automatycznie untabbed po wywołaniu OrderOut metody.
  • W kodzie wszystkie okna z kartami są nadal traktowane jako "widoczne", jednak wszystkie karty nienależące do przodu są ukryte przez system przy użyciu technologii CoreGraphics.
  • TabbingIdentifier Użyj właściwości , NSWindow aby zgrupować okna razem w karty.
  • Jeśli jest NSDocument to aplikacja oparta, kilka z tych funkcji zostanie automatycznie włączonych (takich jak przycisk plus dodawany do paska karty) bez żadnej akcji dewelopera.
  • NSDocument Aplikacje nie oparte mogą włączyć przycisk "plus" w grupie kart, aby dodać nowy dokument, przesłaniając GetNewWindowForTab metodę .NSWindowsController

Łącząc wszystkie elementy, aplikacja, która chciała używać systemu z kartami systemu Windows, AppDelegate może wyglądać następująco:

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 Gdzie właściwość śledzi liczbę utworzonych nowych dokumentów, a NewDocument metoda tworzy nowy dokument i wyświetla go.

Następnie NSWindowController może wyglądać następująco:

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

Gdzie właściwość statyczna App udostępnia skrót, aby przejść do obiektu AppDelegate. Metoda SetDefaultDocumentTitle ustawia nowy tytuł dokumentów na podstawie liczby utworzonych nowych dokumentów.

Poniższy kod informuje system macOS, że aplikacja woli używać kart i udostępnia ciąg umożliwiający grupowanie systemu Windows aplikacji na karty:

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

A następująca metoda zastąpienia dodaje przycisk plus na pasku kart, który utworzy nowy dokument po kliknięciu przez użytkownika:

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

Korzystanie z animacji podstawowej

Core Animation to aparat renderowania grafiki o wysokiej mocy, który jest wbudowany w system macOS. Animacja core została zoptymalizowana pod kątem korzystania z procesora GPU (jednostki przetwarzania grafiki) dostępnego w nowoczesnym sprzęcie z systemem macOS, w przeciwieństwie do uruchamiania operacji graficznych na procesorze CPU, co może spowolnić maszynę.

Element CALayer, udostępniany przez animację core, może służyć do zadań, takich jak szybkie i płynne przewijanie i animacje. Interfejs użytkownika aplikacji powinien składać się z wielu widoków podrzędnych i warstw, aby w pełni korzystać z animacji core.

Obiekt CALayer udostępnia kilka właściwości, które umożliwiają deweloperowi kontrolowanie zawartości wyświetlanej na ekranie użytkownikowi, takiej jak:

  • Content — może być elementem NSImage lub CGImage , który udostępnia zawartość warstwy.
  • BackgroundColor - Ustawia kolor tła warstwy jako CGColor
  • BorderWidth - Ustawia szerokość obramowania.
  • BorderColor - Ustawia kolor obramowania.

Aby korzystać z podstawowej grafiki w interfejsie użytkownika aplikacji, musi używać widoków opartych na warstwie, co firma Apple sugeruje, że deweloper powinien zawsze włączać widok zawartości okna. Dzięki temu wszystkie widoki podrzędne będą automatycznie dziedziczyć również tworzenie kopii zapasowych warstw.

Ponadto firma Apple sugeruje użycie widoków opartych na warstwie, a nie do dodawania nowej CALayer warstwy jako podwarstwowej, ponieważ system automatycznie obsłuży kilka wymaganych ustawień (takich jak te wymagane przez wyświetlacz Siatkówki).

Tworzenie kopii zapasowych warstw można włączyć, ustawiając WantsLayerNSView element na lub true wewnątrz konstruktora interfejsu Xcode w obszarze Inspektor efektów widoku, sprawdzając warstwę animacji podstawowej:

Inspektor efektów widoku

Ponowne rysowanie widoków za pomocą warstw

Innym ważnym krokiem w przypadku korzystania z widoków opartych na warstwie w aplikacji Xamarin.Mac jest ustawienie LayerContentsRedrawPolicyNSView wartości na OnSetNeedsDisplay wartość w elem.NSViewController Na przykład:

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

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

Jeśli deweloper nie ustawi tej właściwości, widok zostanie ponownie wyrysy po zmianie źródła ramki, co nie jest pożądane ze względu na wydajność. Jeśli ta właściwość jest ustawiona na OnSetNeedsDisplay dewelopera, trzeba będzie ręcznie ustawić NeedsDisplay wartość , aby true wymusić ponowne rysowanie zawartości.

Gdy widok jest oznaczony jako zanieczyszczony, system sprawdza WantsUpdateLayer właściwość Widoku. Jeśli zwraca true metodę UpdateLayer , wywoływana jest metoda , a w przeciwnym razie DrawRect metoda widoku jest wywoływana w celu zaktualizowania zawartości widoku.

Firma Apple oferuje następujące sugestie dotyczące aktualizowania zawartości widoków, jeśli jest to wymagane:

  • Apple woli używać UpdateLaterDrawRect za każdym razem, gdy jest to możliwe, ponieważ zapewnia znaczący wzrost wydajności.
  • Użyj tego samego layer.Contents w przypadku elementów interfejsu użytkownika, które wyglądają podobnie.
  • Firma Apple preferuje również deweloperowi tworzenie interfejsu użytkownika przy użyciu widoków standardowych, takich jak NSTextField, zawsze, gdy jest to możliwe.

Aby użyć metody UpdateLayer, utwórz klasę niestandardową dla NSView klasy i utwórz kod w następujący sposób:

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

Korzystanie z nowoczesnego przeciągania i upuszczania

Aby przedstawić nowoczesne środowisko przeciągania i upuszczania dla użytkownika, deweloper powinien wdrożyć przeciąganie stada w operacjach przeciągania i upuszczania aplikacji. Przeciągnij element Flocking , gdzie każdy przeciągnięty plik lub element początkowo jest wyświetlany jako pojedynczy element, który gromadzi (grupuje się razem pod kursorem z liczbą elementów), gdy użytkownik kontynuuje operację przeciągania.

Jeśli użytkownik zakończy operację Przeciąganie, poszczególne elementy zostaną coflock i powrócą do oryginalnych lokalizacji.

Poniższy przykładowy kod umożliwia przeciąganie flockingu w widoku niestandardowym:

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

Efekt stada został osiągnięty przez wysłanie każdego elementu przeciągniętego do BeginDraggingSession metody jako NSView oddzielnego elementu w tablicy.

Podczas pracy z klasą NSTableView lub NSOutlineViewużyj PastboardWriterForRow metody NSTableViewDataSource klasy , aby uruchomić operację Przeciąganie:

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

Dzięki temu deweloper może podać jednostkę NSDraggingItem dla każdego elementu w tabeli, który jest przeciągany, w przeciwieństwie do starszej metody WriteRowsWith , która zapisuje wszystkie wiersze jako pojedynczą grupę do tablicy wklejanej.

Podczas pracy z NSCollectionViewsmetodą ponownie użyj PasteboardWriterForItemAt metody , a nie WriteItemsAt metody podczas przeciągania.

Deweloper powinien zawsze unikać umieszczania dużych plików na tablicy wklejanych. Nowość w systemie macOS Sierra, Obietnice plików umożliwiają deweloperowi umieszczenie odwołań do danych plików w tablicy wklejanej, która zostanie później spełniona, gdy użytkownik zakończy operację Drop przy użyciu nowych NSFilePromiseProvider i NSFilePromiseReceiver klas.

Korzystanie z nowoczesnego śledzenia zdarzeń

W przypadku elementu interfejsu użytkownika (takiego jak NSButton) dodanego do obszaru Tytuł lub Pasek narzędzi użytkownik powinien mieć możliwość kliknięcia elementu i wyzwolenia zdarzenia jako normalnego (na przykład wyświetlania okna podręcznego). Jednak ponieważ element znajduje się również w obszarze Tytuł lub Pasek narzędzi, użytkownik powinien mieć możliwość kliknięcia i przeciągnięcia elementu w celu przeniesienia okna.

Aby to osiągnąć w kodzie, utwórz klasę niestandardową dla elementu (na przykład NSButton) i przesłoń MouseDown zdarzenie w następujący sposób:

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

Ten kod używa TrackEventsMatching metody NSWindow , którą element interfejsu użytkownika jest dołączony do przechwytywania zdarzeń LeftMouseUp i LeftMouseDragged . LeftMouseUp W przypadku zdarzenia element interfejsu użytkownika odpowiada normalnie. LeftMouseDragged W przypadku zdarzenia zdarzenie jest przekazywane do NSWindowmetody "sPerformWindowDrag", aby przenieść okno na ekranie.

PerformWindowDrag Wywołanie metody NSWindow klasy zapewnia następujące korzyści:

  • Umożliwia przenoszenie okna, nawet jeśli aplikacja jest zawieszona (na przykład podczas przetwarzania pętli głębokiej).
  • Przełączanie miejsca będzie działać zgodnie z oczekiwaniami.
  • Pasek spacji będzie wyświetlany normalnie.
  • Przyciąganie okien i wyrównanie działają normalnie.

Korzystanie z nowoczesnych kontrolek widoku kontenera

System macOS Sierra oferuje wiele nowoczesnych ulepszeń istniejących kontrolek widoku kontenerów dostępnych w poprzedniej wersji systemu operacyjnego.

Ulepszenia widoku tabeli

Deweloper powinien zawsze używać nowej NSView opartej wersji kontrolek widoku kontenera, takiej jak NSTableView. Na przykład:

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

Umożliwia to dołączanie niestandardowych akcji wierszy tabeli do podanych wierszy w tabeli (na przykład przesuwanie w prawo do usunięcia wiersza). Aby włączyć to zachowanie, zastąpij metodę RowActions elementu 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 Statyczny element służy do tworzenia nowej akcji wiersza tabeli następujących stylów:

  • Regular - Wykonuje standardową niedestrukcyjną akcję, taką jak edytowanie zawartości wiersza.
  • Destructive — Wykonuje destrukcyjną akcję, taką jak usuwanie wiersza z tabeli. Te akcje będą renderowane z czerwonym tłem.

Ulepszenia widoku przewijania

Jeśli używasz widoku przewijania (NSScrollView) bezpośrednio lub w ramach innej kontrolki (takiej jak NSTableView), zawartość widoku przewijania może być przesuwana w obszarze Tytuł i Pasek narzędzi w aplikacji Xamarin.Mac przy użyciu nowoczesnego wyglądu i widoków.

W związku z tym pierwszy element w obszarze zawartości Widok przewijania może być częściowo zasłonięty przez obszar Tytuł i Pasek narzędzi.

Aby rozwiązać ten problem, firma Apple dodała do klasy dwie nowe właściwości NSScrollView :

  • ContentInsets — Umożliwia deweloperowi podanie NSEdgeInsets obiektu definiującego przesunięcie, które zostanie zastosowane w górnej części widoku przewijania.
  • AutomaticallyAdjustsContentInsets — Jeśli true widok przewijania automatycznie obsłuży ContentInsets element dla dewelopera.

Za pomocą ContentInsets dewelopera można dostosować początek widoku przewijania, aby umożliwić włączenie akcesoriów, takich jak:

  • Wskaźnik sortowania, taki jak wskaźnik pokazany w aplikacji Poczta.
  • Pole wyszukiwania.
  • Przycisk Odśwież lub Aktualizuj.

Automatyczne rozmieszczanie i lokalizowanie w nowoczesnych aplikacjach

Firma Apple wprowadziła kilka technologii w środowisku Xcode, które umożliwiają deweloperowi łatwe tworzenie międzynarodowych aplikacji dla systemu macOS. Program Xcode umożliwia deweloperowi oddzielenie tekstu dostępnego dla użytkownika od projektu interfejsu użytkownika aplikacji w plikach scenorysu i udostępnia narzędzia do utrzymania tego oddzielenia, jeśli interfejs użytkownika ulegnie zmianie.

Aby uzyskać więcej informacji, zobacz Przewodnik po internacjonalizacji i lokalizacji firmy Apple.

Implementowanie internacjonalizacji bazowej

Wdrażając podstawową międzynarodowość, deweloper może udostępnić pojedynczy plik Scenorysu reprezentujący interfejs użytkownika aplikacji i oddzielić wszystkie ciągi dostępne dla użytkownika.

Gdy deweloper tworzy początkowy plik scenorysu (lub pliki), który definiuje interfejs użytkownika aplikacji, zostanie utworzony w podstawowej internationalizacji (języku, w jakim mówi deweloper).

Następnie deweloper może eksportować lokalizacje i podstawowe ciągi międzynarodowych (w projekcie interfejsu użytkownika scenorysu), które można przetłumaczyć na wiele języków.

Później te lokalizacje można zaimportować, a program Xcode wygeneruje pliki ciągów specyficzne dla języka dla scenorysu.

Implementowanie automatycznego układu w celu obsługi lokalizacji

Ze względu na to, że zlokalizowane wersje wartości ciągów mogą mieć znacznie różne rozmiary i/lub kierunek odczytu, deweloper powinien użyć automatycznego układu, aby ustawić i określić rozmiar interfejsu użytkownika aplikacji w pliku Scenorysu.

Firma Apple sugeruje wykonanie następujących czynności:

  • Usuń ograniczenia o stałej szerokości — wszystkie widoki tekstowe powinny mieć możliwość zmiany rozmiaru na podstawie ich zawartości. Widok o stałej szerokości może przycinać zawartość w określonych językach.
  • Użyj wewnętrznych rozmiarów zawartości — domyślnie widoki oparte na tekście automatycznie dopasowują rozmiar do zawartości. W przypadku widoku opartego na tekście, który nie jest poprawnie ustawiany, zaznacz je w konstruktorze interfejsu Xcode, a następnie wybierz pozycję Edytuj>rozmiar do dopasowania zawartości.
  • Zastosuj atrybuty wiodące i końcowe — ponieważ kierunek tekstu może ulec zmianie w zależności od języka użytkownika, użyj nowych Leading atrybutów ograniczeń i Trailing w przeciwieństwie do istniejących Right atrybutów i Left . Leading i Trailing automatycznie dostosuje się w zależności od kierunku języków.
  • Przypinanie widoków do sąsiednich widoków — umożliwia to zmianę położenia i zmiany rozmiaru widoków w miarę zmiany widoków w odpowiedzi na wybrany język.
  • Nie ustawiaj minimalnych i/lub maksymalnych rozmiarów systemu Windows — zezwalaj systemowi Windows na zmianę rozmiaru, ponieważ wybrany język zmienia rozmiar swoich obszarów zawartości.
  • Stale zmienia się układ testowy — podczas programowania w aplikacji należy stale testować w różnych językach. Aby uzyskać więcej informacji, zobacz dokumentację testowania aplikacji międzynarodowych firmy Apple.
  • Użyj elementów NSStackView do przypinania widoków razem - NSStackViews , dzięki czemu ich zawartość może zmieniać i rozwijać się w przewidywalny sposób, a rozmiar zawartości zmienia się na podstawie wybranego języka.

Lokalizowanie w narzędziu Xcode Interface Builder

Firma Apple udostępnia kilka funkcji w narzędziu Interface Builder programu Xcode, których deweloper może używać podczas projektowania lub edytowania interfejsu użytkownika aplikacji w celu obsługi lokalizacji. Sekcja Text Direction (Kierunek tekstu) inspektora atrybutów umożliwia deweloperowi podanie wskazówek dotyczących sposobu użycia kierunku i aktualizacji w wybranym widoku tekstowym (na przykład NSTextField):

Opcje Kierunek tekstu

Istnieją trzy możliwe wartości kierunku tekstu:

  • Naturalny — układ jest oparty na ciągu przypisanym do kontrolki.
  • Od lewej do prawej — układ jest zawsze wymuszany od lewej do prawej.
  • Od prawej do lewej — układ jest zawsze wymuszany od prawej do lewej.

Układ ma dwie możliwe wartości:

  • Od lewej do prawej — układ jest zawsze od lewej do prawej.
  • Od prawej do lewej — układ jest zawsze od prawej do lewej.

Zazwyczaj nie należy ich zmieniać, chyba że wymagane jest określone wyrównanie.

Właściwość Mirror informuje system o przerzucaniu określonych właściwości kontrolki (takich jak położenie obrazu komórki). Ma trzy możliwe wartości:

  • Automatycznie — pozycja zostanie automatycznie zmieniona na podstawie kierunku wybranego języka.
  • W interfejsie od prawej do lewej — pozycja zostanie zmieniona tylko w językach od prawej do lewej.
  • Nigdy — pozycja nigdy się nie zmieni.

Jeśli deweloper określił pozycję Centrum, Wyjustuj lub Pełne wyrównanie zawartości w widoku tekstowym, nigdy nie zostanie on przerzucony na podstawie wybranego języka.

Przed systemem macOS Sierra kontrolki utworzone w kodzie nie będą dublowane automatycznie. Deweloper musiał użyć kodu, takiego jak poniżej, aby obsługiwać dublowanie:

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

Gdzie kontrolka Alignment i ImagePosition jest ustawiana na UserInterfaceLayoutDirection podstawie kontrolki .

System macOS Sierra dodaje kilka nowych konstruktorów wygody (za pośrednictwem metody statycznej CreateButton ), które przyjmują kilka parametrów (takich jak Tytuł, Obraz i Akcja) i będą automatycznie dublowanie poprawnie. Na przykład:

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

Korzystanie z wyglądu systemu

Nowoczesne aplikacje systemu macOS mogą przyjąć nowy wygląd ciemnego interfejsu, który dobrze sprawdza się w przypadku tworzenia, edytowania lub prezentacji aplikacji:

Przykład ciemnego interfejsu użytkownika okna dla komputerów Mac

Można to zrobić, dodając jeden wiersz kodu przed przedstawieniem okna. Na przykład:

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 Statyczna metoda NSAppearance klasy służy do pobierania nazwanego wyglądu z systemu (w tym przypadku NSAppearance.NameVibrantDark).

Firma Apple ma następujące sugestie dotyczące korzystania z wyglądu systemu:

  • Preferuj nazwane kolory dla wartości zakodowanych na stałe (takich jak LabelColor i SelectedControlColor).
  • W miarę możliwości używaj standardowego stylu sterowania systemu.

Aplikacja systemu macOS korzystająca z wyglądu systemu automatycznie będzie działać poprawnie dla użytkowników, którzy włączyli funkcje ułatwień dostępu z poziomu aplikacji Preferencje systemowe. W rezultacie firma Apple sugeruje, że deweloper powinien zawsze używać wyglądów systemowych w aplikacjach systemu macOS.

Projektowanie interfejsów użytkownika za pomocą scenorysów

Scenorysy pozwalają deweloperowi nie tylko zaprojektować poszczególne elementy tworzące interfejs użytkownika aplikacji, ale także wizualizować i projektować przepływ interfejsu użytkownika oraz hierarchię danych elementów.

Kontrolery umożliwiają deweloperowi zbieranie elementów do jednostki kompozycji i abstrakcji Segues oraz usuwanie typowego "kodu kleju" wymaganego do przenoszenia w całej hierarchii widoków:

Edytowanie interfejsu użytkownika w narzędziu Xcode Interface Builder

Aby uzyskać więcej informacji, zobacz dokumentację Wprowadzenie do scenorysów .

Istnieje wiele wystąpień, w których dana scena zdefiniowana w scenorysie będzie wymagać danych z poprzedniej sceny w hierarchii widoków. Firma Apple ma następujące sugestie dotyczące przekazywania informacji między scenami:

  • Zależności danych powinny zawsze być kaskadowe w dół przez hierarchię.
  • Unikaj sztywnego kodowania zależności strukturalnych interfejsu użytkownika, ponieważ ogranicza to elastyczność interfejsu użytkownika.
  • Użyj interfejsów języka C#, aby zapewnić ogólne zależności danych.

Kontroler widoku, który działa jako źródło Segue, może zastąpić PrepareForSegue metodę i wykonać wszelkie wymagane inicjowanie (takie jak przekazywanie danych) przed wykonaniem segue w celu wyświetlenia docelowego kontrolera widoku. Na przykład:

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

Aby uzyskać więcej informacji, zobacz dokumentację segues.

Propagacja akcji

Na podstawie projektu aplikacji systemu macOS może wystąpić czas, kiedy najlepszą procedurę obsługi akcji w kontrolce interfejsu użytkownika może znajdować się w innym miejscu w hierarchii interfejsu użytkownika. Zwykle dotyczy to menu i elementów menu, które znajdują się we własnej scenie, niezależnie od pozostałej części interfejsu użytkownika aplikacji.

Aby obsłużyć tę sytuację, deweloper może utworzyć akcję niestandardową i przekazać akcję w górę łańcucha odpowiedzi. Aby uzyskać więcej informacji, zobacz dokumentację Dotyczącą pracy z niestandardowymi akcjami okien.

Nowoczesne funkcje dla komputerów Mac

Firma Apple oferuje kilka funkcji przeznaczonych dla użytkowników w systemie macOS Sierra, które umożliwiają deweloperowi korzystanie z platformy Mac, takich jak:

  • NSUserActivity — umożliwia aplikacji opisywanie działania, w które aktualnie uczestniczy użytkownik. NSUserActivity został utworzony w celu obsługi funkcji HandOff, gdzie można pobrać działanie na jednym z urządzeń użytkownika i kontynuować na innym urządzeniu. NSUserActivity działa tak samo w systemie macOS, jak w systemie iOS, dlatego zapoznaj się z naszą dokumentacją Wprowadzenie do przekazywania systemu iOS, aby uzyskać więcej informacji.
  • Siri na komputerze Mac — Siri używa bieżącego działania (NSUserActivity), aby zapewnić kontekst poleceń, które użytkownik może wydać.
  • Przywracanie stanu — gdy użytkownik zamyka aplikację w systemie macOS, a następnie uruchamia ją ponownie, aplikacja zostanie automatycznie zwrócona do poprzedniego stanu. Deweloper może używać interfejsu API przywracania stanu do kodowania i przywracania przejściowych stanów interfejsu użytkownika przed wyświetleniem interfejsu użytkownika użytkownikowi. Jeśli aplikacja jest NSDocument oparta, przywracanie stanu jest obsługiwane automatycznie. Aby włączyć przywracanie stanu dla aplikacji innychNSDocument niż oparte, ustaw RestorableNSWindow dla klasy wartość true.
  • Dokumenty w chmurze — przed macOS Sierra aplikacja musiała jawnie wyrazić zgodę na pracę z dokumentami w usłudze iCloud Drive użytkownika. W systemie macOS Sierra foldery Pulpit i Dokumenty użytkownika mogą być automatycznie synchronizowane z usługą iCloud Drive. W związku z tym lokalne kopie dokumentów mogą zostać usunięte, aby zwolnić miejsce na komputerze użytkownika. NSDocument aplikacje oparte na aplikacjach automatycznie obsłużą tę zmianę. Wszystkie inne typy aplikacji będą musiały używać elementu NSFileCoordinator do synchronizowania odczytu i zapisywania dokumentów.

Podsumowanie

W tym artykule opisano kilka wskazówek, funkcji i technik, których deweloper może użyć do utworzenia nowoczesnej aplikacji systemu macOS na platformie Xamarin.Mac.