Share via


Erstellen moderner macOS-Apps

In diesem Artikel werden mehrere Tipps, Features und Techniken behandelt, die ein Entwickler zum Erstellen einer modernen macOS-App in Xamarin.Mac verwenden kann.

Erstellen moderner Designs mit modernen Ansichten

Ein modernes Aussehen enthält eine moderne Fenster- und Symbolleistendarstellung, z. B. die unten gezeigte Beispiel-App:

Beispiel für eine moderne Mac-App-UI

Aktivieren von Inhaltsansichten in voller Größe

Um dies in einer Xamarin.Mac-App zu erreichen, möchte der Entwickler eine Inhaltsansicht mit voller Größe verwenden, was bedeutet, dass sich der Inhalt unter den Bereichen Tool und Titelleiste erweitert und von macOS automatisch verschwommen wird.

Um dieses Feature im Code zu aktivieren, erstellen Sie eine benutzerdefinierte Klasse für die NSWindowController und lassen sie wie folgt aussehen:

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

Dieses Feature kann auch im Benutzeroberflächen-Generator von Xcode aktiviert werden, indem sie das Fenster auswählen und die Inhaltsansicht in voller Größe überprüfen:

Bearbeiten des Standard Storyboards im Benutzeroberflächen-Generator von Xcode

Wenn Sie eine Inhaltsansicht mit voller Größe verwenden, muss der Entwickler den Inhalt möglicherweise unter den Titel- und Symbolleistenbereichen versatzen, sodass bestimmte Inhalte (z. B. Bezeichnungen) nicht darunter geschoben werden.

Um dieses Problem zu erschweren, können die Bereiche "Titel" und "Toolleiste" eine dynamische Höhe basierend auf der Aktion aufweisen, die der Benutzer gerade ausführt, die Version von macOS, die der Benutzer installiert hat, und/oder die Mac-Hardware, auf der die App ausgeführt wird.

Daher funktioniert das Einfache hartcodieren des Offsets beim Gestalten der Benutzeroberfläche nicht. Der Entwickler muss einen dynamischen Ansatz verfolgen.

Apple enthält die Key-Value Observable-EigenschaftContentLayoutRect der NSWindow Klasse, um den aktuellen Inhaltsbereich im Code abzurufen. Der Entwickler kann diesen Wert verwenden, um die erforderlichen Elemente manuell zu positionieren, wenn sich der Inhaltsbereich ändert.

Die bessere Lösung ist die Verwendung von AutoLayout- und Größenklassen, um die UI-Elemente entweder im Code oder im Schnittstellen-Generator zu positionieren.

Code wie das folgende Beispiel kann verwendet werden, um UI-Elemente mithilfe von AutoLayout- und Größenklassen im Ansichtscontroller der App zu positionieren:

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

Dieser Code erstellt Speicher für eine obere Einschränkung, die auf eine Bezeichnung (ItemTitle) angewendet wird, um sicherzustellen, dass sie nicht unter den Bereich "Titel" und "Symbolleiste" fällt:

public NSLayoutConstraint topConstraint { get; set; }

Durch Überschreiben der Methode des Ansichtscontrollers UpdateViewConstraints kann der Entwickler testen, ob die erforderliche Einschränkung bereits erstellt und bei Bedarf erstellt wurde.

Wenn eine neue Einschränkung erstellt werden muss, wird auf die ContentLayoutGuide Eigenschaft des Window-Steuerelements zugegriffen, auf das eingeschränkt werden muss, und in eine NSLayoutGuide:

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

Auf die TopAnchor-Eigenschaft der Datei NSLayoutGuide wird zugegriffen, und wenn sie verfügbar ist, wird sie verwendet, um eine neue Einschränkung mit dem gewünschten Offsetbetrag zu erstellen, und die neue Einschränkung wird aktiviert, um sie anzuwenden:

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

Aktivieren optimierter Symbolleisten

Ein normales macOS-Fenster enthält eine standardmäßige Titelleiste, die am oberen Rand des Fensters ausgeführt wird. Wenn das Fenster auch eine Symbolleiste enthält, wird es unter diesem Titelleistenbereich angezeigt:

Eine Standardmäßige Mac-Symbolleiste

Wenn Sie eine optimierte Symbolleiste verwenden, verschwindet der Titelbereich, und die Symbolleiste wird in der Position der Titelleiste in Übereinstimmung mit den Schaltflächen "Fenster schließen", "Minimieren" und "Maximieren" nach oben verschoben:

Eine optimierte Mac-Symbolleiste

Die optimierte Symbolleiste wird durch Überschreiben der ViewWillAppear Methode der NSViewController optimierten Symbolleiste aktiviert und sieht wie folgt aus:

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

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

Dieser Effekt wird in der Regel für Shoebox-Anwendungen (eine Fenster-Apps) wie Karten, Kalender, Notizen und Systemeinstellungen verwendet.

Verwenden von Zubehöransichtscontrollern

Je nach Design der App möchte der Entwickler möglicherweise auch den Titelleistenbereich durch einen Zubehör-Ansichtscontroller ergänzen, der direkt unterhalb des Bereichs "Titel-/Toolleiste" angezeigt wird, um kontextabhängige Steuerelemente für den Benutzer basierend auf der aktivität bereitzustellen, an der sie derzeit beteiligt sind:

Beispiel für den Ansichtscontroller für Zubehör

Der Zubehöransichtscontroller wird automatisch verschwommen und die Größe des Systems ohne Eingreifen des Entwicklers geändert.

Gehen Sie wie folgt vor, um einen Zubehör-Ansichtscontroller hinzuzufügen:

  1. Doppelklicken Sie im Projektmappen-Explorer auf die Datei Main.storyboard, um sie zur Bearbeitung zu öffnen.

  2. Ziehen Sie einen benutzerdefinierten Ansichtscontroller in die Hierarchie des Fensters:

    Hinzufügen eines neuen benutzerdefinierten Ansichtscontrollers

  3. Layout der Ui der Zubehöransicht:

    Entwerfen der neuen Ansicht

  4. Machen Sie die Zubehöransicht als Steckdose und alle anderen Aktionen oder Outlets für die Benutzeroberfläche verfügbar:

    Hinzufügen des erforderlichen OUtlet

  5. Speichern Sie die Änderungen.

  6. Kehren Sie zu Visual Studio für Mac zurück, um die Änderungen zu synchronisieren.

Bearbeiten Sie das NSWindowController Und machen Sie es wie folgt aussehen:

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

Die wichtigsten Punkte dieses Codes sind der Ort, an dem die Ansicht auf die benutzerdefinierte Ansicht festgelegt ist, die im Schnittstellen-Generator definiert und als Steckdose verfügbar gemacht wurde:

accessoryView.View = AccessoryViewGoBar;

Und das LayoutAttribute definiert , wo das Zubehör angezeigt wird:

accessoryView.LayoutAttribute = NSLayoutAttribute.Bottom;

Da macOS jetzt vollständig lokalisiert ist, sind die Left Eigenschaften RightNSLayoutAttribute veraltet und sollten durch Leading und Trailingersetzt werden.

Verwenden von Windows-Registerkarten

Darüber hinaus kann das macOS-System Zubehör-Ansichtscontroller zum Fenster der App hinzufügen. Um beispielsweise Tabbed Windows zu erstellen, in dem mehrere der Windows-Apps in einem virtuellen Fenster zusammengeführt werden:

Beispiel für ein Mac-Fenster mit Registerkarten

In der Regel muss der Entwickler in seinen Xamarin.Mac-Apps tabbed Windows verwenden, das System behandelt sie automatisch wie folgt:

  • Windows wird beim Aufrufen der OrderFront Methode automatisch Tabstopps angezeigt.
  • Windows wird beim Aufrufen der OrderOut Methode automatisch untabbediert.
  • Im Code werden alle Tabbed-Fenster weiterhin als "sichtbar" betrachtet, jedoch werden alle Nicht-Front-Tabs vom System mit CoreGraphics ausgeblendet.
  • Verwenden Sie die TabbingIdentifier Eigenschaft, NSWindow um Windows in Registerkarten zu gruppieren.
  • Wenn es sich um eine NSDocument basierte App handelt, werden mehrere dieser Features automatisch aktiviert (z. B. die Plusschaltfläche, die der Registerkartenleiste hinzugefügt wird) ohne Entwickleraktion.
  • NSDocument Nicht basierende Apps können die Schaltfläche "Plus" in der Registerkartengruppe aktivieren, um ein neues Dokument hinzuzufügen, indem die GetNewWindowForTab Methode der NSWindowsController.

Wenn Sie alle Teile zusammenführen, könnte die AppDelegate App, die systembasierte Windows-Registerkarten verwenden wollte, wie folgt aussehen:

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

Wo die NewDocumentNumber Eigenschaft die Anzahl der neuen Dokumente nachverfolgt, die erstellt wurden, und die NewDocument Methode erstellt ein neues Dokument und zeigt es an.

Die NSWindowController könnte dann wie folgt aussehen:

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

Wo die statische App Eigenschaft eine Verknüpfung zum Aufrufen der AppDelegate. Die SetDefaultDocumentTitle Methode legt basierend auf der Anzahl der erstellten neuen Dokumente einen neuen Dokumenttitel fest.

Der folgende Code teilt macOS mit, dass die App Registerkarten bevorzugt, und stellt eine Zeichenfolge bereit, mit der die Windows-App in Registerkarten gruppiert werden kann:

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

Die folgende Überschreibungsmethode fügt der Registerkartenleiste eine Plusschaltfläche hinzu, die ein neues Dokument erstellt, wenn der Benutzer darauf klickt:

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

Verwenden der Kernanimation

Kernanimation ist ein leistungsstarkes Grafikrenderingmodul, das in macOS integriert ist. Die Kernanimation wurde optimiert, um die GPU (Graphics Processing Unit) zu nutzen, die in moderner macOS-Hardware verfügbar ist, anstatt die Grafikvorgänge auf der CPU auszuführen, was den Computer verlangsamen kann.

Die CALayervon der Kernanimation bereitgestellte Animation kann für Aufgaben wie schnelles und flüssiges Scrollen und Animationen verwendet werden. Die Benutzeroberfläche einer App sollte aus mehreren Unteransichten und Ebenen bestehen, um die Hauptanimation vollständig nutzen zu können.

Ein CALayer Objekt bietet mehrere Eigenschaften, mit denen der Entwickler steuern kann, was dem Benutzer auf dem Bildschirm angezeigt wird, z. B.:

  • Content - Dies kann ein NSImage oder CGImage der den Inhalt der Ebene bereitstellt.
  • BackgroundColor - Legt die Hintergrundfarbe der Ebene als eine CGColor
  • BorderWidth - Legt die Rahmenbreite fest.
  • BorderColor - Legt die Rahmenfarbe fest.

Um Kerngrafiken in der Benutzeroberfläche der App zu verwenden, muss sie Layer Backed Views verwenden, was Apple vorschlägt, dass der Entwickler immer in der Inhaltsansicht des Fensters aktivieren sollte. Auf diese Weise erben alle untergeordneten Ansichten automatisch auch layer backing.

Darüber hinaus schlägt Apple vor, layer backed Views zu verwenden, anstatt eine neue CALayer als Unterlayer hinzuzufügen, da das System automatisch mehrere der erforderlichen Einstellungen verarbeitet (z. B. diejenigen, die von einem Retina Display benötigt werden).

Die Layersicherung kann aktiviert werden, indem sie den WantsLayer Schnittstellen-Generator von NSViewtrue Xcode im Ansichtseffekt-Inspektor festlegen, indem Sie die Kernanimationsschicht überprüfen:

Ansichtseffekte-Inspektor

Neuauszeichnen von Ansichten mit Ebenen

Ein weiterer wichtiger Schritt bei der Verwendung von Layer Backed Views in einer Xamarin.Mac-App ist das LayerContentsRedrawPolicy Festlegen des Werts NSViewOnSetNeedsDisplay in der NSViewController Zum Beispiel:

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

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

Wenn der Entwickler diese Eigenschaft nicht festlegt, wird die Ansicht immer neu gezeichnet, wenn sich der Frameursprung ändert, was aus Leistungsgründen nicht gewünscht wird. Wenn diese Eigenschaft auf OnSetNeedsDisplay den Entwickler festgelegt ist, muss dies manuell festgelegt werden NeedsDisplay , damit true der Inhalt jedoch neu gezeichnet wird.

Wenn eine Ansicht als modifiziert markiert ist, überprüft das System die WantsUpdateLayer Eigenschaft der Ansicht. Wenn die Methode zurückgegeben true wird, wird die UpdateLayer Methode aufgerufen, andernfalls wird die DrawRect Methode der Ansicht aufgerufen, um den Inhalt der Ansicht zu aktualisieren.

Apple hat bei Bedarf die folgenden Vorschläge zum Aktualisieren eines Views-Inhalts:

  • Apple bevorzugt die Verwendung UpdateLater nach Möglichkeit, DrawRect da es eine erhebliche Leistungssteigerung bietet.
  • Verwenden Sie dasselbe layer.Contents für UI-Elemente, die ähnlich aussehen.
  • Apple bevorzugt auch den Entwickler, seine Benutzeroberfläche mithilfe von Standardansichten zu verfassen, z NSTextField. B. immer wieder.

Erstellen Sie zum Verwenden UpdateLayereine benutzerdefinierte Klasse für den NSView Code, und gestalten Sie den Code wie folgt:

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

Verwenden des modernen Ziehens und Ablegens

Um dem Benutzer eine moderne Drag- und Drop-Oberfläche zu bieten, sollte der Entwickler Drag Flocking in den Drag and Drop-Vorgängen ihrer App übernehmen. Drag Flocking ist der Ort, an dem jede einzelne Datei oder jedes Element, das gezogen wird, anfangs als einzelnes Element angezeigt wird, das sich zusammen unter dem Cursor mit einer Anzahl der Elemente gruppieren kann, während der Benutzer den Ziehvorgang fortsetzt.

Wenn der Benutzer den Drag-Vorgang beendet, werden die einzelnen Elemente entflockt und zu ihren ursprünglichen Positionen zurückkehren.

Im folgenden Beispielcode wird Drag Flocking in einer benutzerdefinierten Ansicht aktiviert:

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

Der Herdeeffekt wurde erreicht, indem jedes Element an die BeginDraggingSession Methode des NSView als separates Element in einem Array gezogen wird.

Verwenden Sie beim Arbeiten mit einer oder oder NSTableViewNSOutlineViewder Klasse die PastboardWriterForRow Methode, NSTableViewDataSource um den Dragging-Vorgang zu starten:

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

Dadurch kann der Entwickler eine Einzelperson NSDraggingItem für jedes Element in der Tabelle bereitstellen, das gezogen wird, im Gegensatz zu der älteren Methode WriteRowsWith , die alle Zeilen als einzelne Gruppe in das Einfügebrett schreibt.

Verwenden Sie beim Arbeiten mit NSCollectionViewsder Methode erneut die PasteboardWriterForItemAt Methode im Gegensatz zu der WriteItemsAt Methode, wenn das Ziehen beginnt.

Der Entwickler sollte immer vermeiden, große Dateien auf dem Einfügebrett zu platzieren. New to macOS Sierra, File Promises allow the developer to place references to given files on the pasteboard that will later be erfüllt when the user finishes the Drop operation using the new NSFilePromiseProvider and NSFilePromiseReceiver classes.

Verwenden der modernen Ereignisverfolgung

Bei einem Benutzeroberflächenelement (z. B. a NSButton), das einem Titel- oder Toolleistenbereich hinzugefügt wurde, sollte der Benutzer auf das Element klicken und ein Ereignis als normal auslösen lassen (z. B. anzeigen eines Popupfensters). Da sich das Element jedoch auch im Bereich "Titel" oder "Symbolleiste" befindet, sollte der Benutzer auch auf das Element klicken und ziehen können, um das Fenster zu verschieben.

Erstellen Sie dazu im Code eine benutzerdefinierte Klasse für das Element (z NSButton. B. ) und überschreiben Sie das MouseDown Ereignis wie folgt:

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

Dieser Code verwendet die TrackEventsMatching Methode, mit der NSWindow das UI-Element verknüpft ist, um die LeftMouseUp Ereignisse abzufangen LeftMouseDragged . Bei einem LeftMouseUp Ereignis antwortet das UI-Element normal. Für das LeftMouseDragged Ereignis wird das Ereignis an die NSWindow's PerformWindowDrag Methode übergeben, um das Fenster auf dem Bildschirm zu verschieben.

Das Aufrufen der PerformWindowDrag Methode der NSWindow Klasse bietet die folgenden Vorteile:

  • Das Fenster kann verschoben werden, auch wenn die App nicht mehr reagiert (z. B. beim Verarbeiten einer Deep-Schleife).
  • Der Leerraumwechsel funktioniert wie erwartet.
  • Die Leerzeichenleiste wird normal angezeigt.
  • Das Ausrichten und Ausrichten von Fenstern funktionieren normal.

Verwenden moderner Containeransichtssteuerelemente

macOS Sierra bietet viele moderne Verbesserungen an den vorhandenen Containeransichtssteuerelementen, die in früheren Versionen des Betriebssystems verfügbar sind.

Verbesserungen der Tabellenansicht

Der Entwickler sollte immer die neue NSView basierte Version von Containeransichtssteuerelementen verwenden, z NSTableView. B. . Zum Beispiel:

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

Dadurch können benutzerdefinierte Tabellenzeilenaktionen an bestimmte Zeilen in der Tabelle angefügt werden (z. B. Wischen nach rechts zum Löschen der Zeile). Um dieses Verhalten zu aktivieren, überschreiben Sie die RowActions Methode der 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
    }
}

Die statische Wird verwendet, um eine neue Tabellenzeilenaktion NSTableViewRowAction.FromStyle der folgenden Formatvorlagen zu erstellen:

  • Regular – Führt eine standardmäßige, nicht destruktive Aktion aus, z. B. das Bearbeiten des Zeileninhalts.
  • Destructive – Führt eine destruktive Aktion aus, z. B. das Löschen der Zeile aus der Tabelle. Diese Aktionen werden mit einem roten Hintergrund gerendert.

Verbesserungen der Bildlaufansicht

Wenn Sie eine Bildlaufansicht (NSScrollView) direkt oder als Teil eines anderen Steuerelements (z NSTableView. B. ) verwenden, kann der Inhalt der Bildlaufansicht unter den Bereichen Titel und Symbolleiste in einer Xamarin.Mac-App mit einem modernen Aussehen und Ansichten schieben.

Daher kann das erste Element im Inhaltsbereich der Bildlaufansicht teilweise vom Bereich "Titel" und "Symbolleiste" verdeckt werden.

Um dieses Problem zu beheben, hat Apple der NSScrollView Klasse zwei neue Eigenschaften hinzugefügt:

  • ContentInsets – Ermöglicht es dem Entwickler, ein NSEdgeInsets Objekt bereitzustellen, das den Offset definiert, der oben in der Bildlaufansicht angewendet wird.
  • AutomaticallyAdjustsContentInsets – Wenn true die Bildlaufansicht den ContentInsets Entwickler automatisch behandelt.

Mithilfe des ContentInsets Entwicklers kann der Start der Bildlaufansicht angepasst werden, um die Aufnahme von Zubehör wie:

  • Eine Sortieranzeige wie die in der Mail-App angezeigte.
  • Ein Suchfeld.
  • Eine Schaltfläche "Aktualisieren" oder "Aktualisieren".

Automatisches Layout und Lokalisierung in modernen Apps

Apple enthält mehrere Technologien in Xcode, mit denen der Entwickler problemlos eine internationalisierte macOS-App erstellen kann. Xcode ermöglicht es dem Entwickler jetzt, benutzergerichteten Text aus dem Benutzeroberflächendesign der App in den Storyboarddateien zu trennen und Tools zum Standard beibehalten, wenn sich die Benutzeroberfläche ändert.

Weitere Informationen finden Sie im Internationalisierungs- und Lokalisierungshandbuch von Apple.

Implementieren der Basis-Internationalisierung

Durch die Implementierung der Base Internationalization kann der Entwickler eine einzelne Storyboarddatei bereitstellen, um die Benutzeroberfläche der App darzustellen und alle benutzerorientierten Zeichenfolgen zu trennen.

Wenn der Entwickler die anfängliche Storyboarddatei (oder Dateien) erstellt, die die Benutzeroberfläche der App definieren, werden sie in der Base Internationalization (die Sprache, die der Entwickler spricht) erstellt.

Als Nächstes kann der Entwickler Lokalisierungen und die Base Internationalization-Zeichenfolgen (im Storyboard-UI-Design) exportieren, die in mehrere Sprachen übersetzt werden können.

Später können diese Lokalisierungen importiert werden, und Xcode generiert die sprachspezifischen Zeichenfolgendateien für das Storyboard.

Implementieren des automatischen Layouts zur Unterstützung der Lokalisierung

Da lokalisierte Versionen von Zeichenfolgenwerten sehr große Größen und/oder Leserichtung aufweisen können, sollte der Entwickler das automatische Layout verwenden, um die Benutzeroberfläche der App in einer Storyboarddatei zu positionieren und zu vergrößern.

Apple schlägt Folgendes vor:

  • Einschränkungen für feste Breite entfernen – Alle textbasierten Ansichten sollten die Größe basierend auf ihrem Inhalt ändern dürfen. Die Ansicht mit fester Breite kann ihre Inhalte in bestimmten Sprachen zuschneiden.
  • Verwenden Sie systeminterne Inhaltsgrößen : Standardmäßig werden textbasierte Ansichten automatisch so angepasst, dass sie ihren Inhalt anpassen. Wählen Sie für textbasierte Ansicht, die nicht ordnungsgemäß angepasst wird, diese im Schnittstellen-Generator von Xcode aus, und wählen Sie dann "Größe an Inhalt anpassen">aus.
  • Anwenden von vorangestellten und nachgestellten Attributen – Da sich die Richtung des Texts basierend auf der Sprache des Benutzers ändern kann, verwenden Sie die neuen Leading und Einschränkungsattribute im Gegensatz zu den vorhandenen Right und TrailingLeft Attributen. Leading und Trailing wird basierend auf der Sprachenrichtung automatisch angepasst.
  • An angrenzende Ansichten anheften – Dies ermöglicht es den Ansichten, die Position zu ändern und ihre Größe zu ändern, wenn sich die Ansichten um sie herum ändern, als Reaktion auf die ausgewählte Sprache.
  • Legen Sie keine Windows-Mindest- und/oder Maximalgrößen fest: Windows kann die Größe ändern, wenn die ausgewählte Sprache die Größe ihrer Inhaltsbereiche ändert.
  • Ständiges Testen des Layouts – Während der Entwicklung bei der App sollte ständig in verschiedenen Sprachen getestet werden. Weitere Details finden Sie in der Dokumentation zu Apple Testing Your Internationalized App .
  • Mit NSStackViews können Sie Ansichten zusammen - NSStackViews anheften, sodass ihre Inhalte auf vorhersehbare Weise verschoben und vergrößert werden können und die Größe der Inhalte basierend auf der ausgewählten Sprache geändert wird.

Lokalisieren im Schnittstellen-Generator von Xcode

Apple hat mehrere Features im Benutzeroberflächen-Generator von Xcode bereitgestellt, die der Entwickler beim Entwerfen oder Bearbeiten der Benutzeroberfläche einer App verwenden kann, um die Lokalisierung zu unterstützen. Der Abschnitt "Textrichtung " des Attributinspektors ermöglicht es dem Entwickler, Hinweise zur Verwendung und Aktualisierung der Richtung in einer ausgewählten textbasierten Ansicht (z NSTextField. B. ):

Die Optionen für die Textrichtung

Es gibt drei mögliche Werte für die Textrichtung:

  • Natürlich – Das Layout basiert auf der Zeichenfolge, die dem Steuerelement zugewiesen ist.
  • Von links nach rechts – Das Layout wird immer nach links nach rechts gezwungen.
  • Von rechts nach links – Das Layout wird immer nach rechts nach links gezwungen.

Für das Layout gibt es zwei mögliche Werte:

  • Von links nach rechts - Das Layout ist immer von links nach rechts.
  • Von rechts nach links - Das Layout ist immer von rechts nach links.

Diese sollten in der Regel nur geändert werden, wenn eine bestimmte Ausrichtung erforderlich ist.

Die Mirror-Eigenschaft weist das System an, bestimmte Steuerelementeigenschaften (z. B. die Zellenbildposition) zu kippen. Es gibt drei mögliche Werte:

  • Automatisch – Die Position ändert sich automatisch basierend auf der richtung der ausgewählten Sprache.
  • In der Schnittstelle von rechts nach links – Die Position wird nur in Rechts-nach-links-basierten Sprachen geändert.
  • Nie - Die Position wird sich nie ändern.

Wenn der Entwickler die Ausrichtung "Center", "Blocksatz" oder "Vollständig" für den Inhalt einer textbasierten Ansicht angegeben hat, werden diese basierend auf der ausgewählten Sprache niemals gekippt.

Vor macOS Sierra wurden steuerelemente, die im Code erstellt wurden, nicht automatisch Spiegel. Der Entwickler musste Code wie den folgenden verwenden, um Spiegel ing zu behandeln:

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

Gibt an, wo das Alignment Steuerelement ImagePosition basierend auf UserInterfaceLayoutDirection dem Steuerelement festgelegt wird.

macOS Sierra fügt mehrere neue Komfortkonstruktoren (über die statische CreateButton Methode) hinzu, die mehrere Parameter (z. B. Title, Image und Action) verwenden und automatisch ordnungsgemäß Spiegel. Zum Beispiel:

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

Verwenden von Systemdarstellungen

Moderne macOS-Apps können ein neues Dunkles Interface-Aussehen übernehmen, das gut für Die Bilderstellung, -bearbeitung oder -präsentations-Apps geeignet ist:

Ein Beispiel für eine dunkle Mac-Fenster-UI

Dazu können Sie eine Codezeile hinzufügen, bevor das Fenster angezeigt wird. Zum Beispiel:

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

Die statische GetAppearance Methode der NSAppearance Klasse wird verwendet, um eine benannte Darstellung aus dem System zu erhalten (in diesem Fall NSAppearance.NameVibrantDark).

Apple hat die folgenden Vorschläge für die Verwendung von Systemdarstellungen:

  • Bevorzugen Sie benannte Farben gegenüber hartcodierten Werten (z LabelColor . B. und SelectedControlColor).
  • Verwenden Sie nach Möglichkeit das Systemstandard-Steuerelementformat.

Eine macOS-App, die die Systemdarstellungen verwendet, funktioniert automatisch ordnungsgemäß für Benutzer, die Barrierefreiheitsfeatures über die Systemeinstellungen-App aktiviert haben. Daher schlägt Apple vor, dass der Entwickler immer Systemdarstellungen in ihren macOS-Apps verwenden sollte.

Entwerfen von UIs mit Storyboards

Storyboards ermöglichen es entwicklern, nicht nur die einzelnen Elemente zu entwerfen, aus denen die Benutzeroberfläche einer App besteht, sondern den UI-Fluss und die Hierarchie der angegebenen Elemente visualisieren und entwerfen.

Controller ermöglichen es dem Entwickler, Elemente in einer Einheit von Komposition und Segues abstrakt zu sammeln und den typischen "Klebecode" zu entfernen, der zum Verschieben in der Ansichtshierarchie erforderlich ist:

Bearbeiten der Benutzeroberfläche im Benutzeroberflächen-Generator von Xcode

Weitere Informationen finden Sie in unserer Dokumentation zur Einführung in Storyboards .

Es gibt viele Instanzen, in denen eine bestimmte Szene, die in einem Storyboard definiert ist, Daten aus einer vorherigen Szene in der Ansichtshierarchie erfordert. Apple hat die folgenden Vorschläge zum Übergeben von Informationen zwischen Szenen:

  • Die Abhängigkeiten von Daten sollten immer nach unten durch die Hierarchie überlappen.
  • Vermeiden Sie strukturelle Abhängigkeiten der Benutzeroberfläche, da dies die Flexibilität der Benutzeroberfläche begrenzt.
  • Verwenden Sie C#-Schnittstellen, um generische Datenabhängige bereitzustellen.

Der Ansichtscontroller, der als Quelle der Segue fungiert, kann die Methode überschreiben und alle PrepareForSegue erforderlichen Initialisierungen (z. B. Das Übergeben von Daten) ausführen, bevor der Segue ausgeführt wird, um den Zielansichtscontroller anzuzeigen. Zum Beispiel:

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

Weitere Informationen finden Sie in unserer Segues-Dokumentation .

Verteilen von Aktionen

Basierend auf dem Design der macOS-App kann es vorkommen, dass sich der beste Handler für eine Aktion für ein UI-Steuerelement an einer anderen Stelle in der UI-Hierarchie befindet. Dies gilt in der Regel für Menüs und Menüelemente, die sich in ihrer eigenen Szene befinden, getrennt von der restlichen Benutzeroberfläche der App.

Um diese Situation zu behandeln, kann der Entwickler eine benutzerdefinierte Aktion erstellen und die Aktion an die Antwortkette übergeben. Weitere Informationen finden Sie in der Dokumentation "Arbeiten mit benutzerdefinierten Fensteraktionen ".

Moderne Mac-Features

Apple hat mehrere benutzerorientierte Features in macOS Sierra enthalten, die es dem Entwickler ermöglichen, die Mac-Plattform optimal zu nutzen, z. B.:

  • NSUserActivity – Auf diese Weise kann die App die Aktivität beschreiben, an der der Benutzer derzeit beteiligt ist. NSUserActivity wurde ursprünglich erstellt, um HandOff zu unterstützen, wo eine Aktivität auf einem der Geräte des Benutzers aufgenommen und auf einem anderen Gerät fortgesetzt werden konnte. NSUserActivity funktioniert in macOS genauso wie in iOS. Weitere Details finden Sie in unserer Dokumentation zur Handoff iOS.
  • Siri auf dem Mac – Siri verwendet die aktuelle Aktivität (NSUserActivity), um Kontext zu den Befehlen bereitzustellen, die ein Benutzer ausgeben kann.
  • Zustandswiederherstellung – Wenn der Benutzer eine App unter macOS beendet und später neu gestartet wird, wird die App automatisch in den vorherigen Zustand zurückgegeben. Der Entwickler kann die Zustandswiederherstellungs-API verwenden, um vorübergehende UI-Zustände zu codieren und wiederherzustellen, bevor die Benutzeroberfläche dem Benutzer angezeigt wird. Wenn die App NSDocument basiert, wird die Zustandswiederherstellung automatisch behandelt. Um die Zustandswiederherstellung für nichtNSDocument basierte Apps zu aktivieren, legen Sie die RestorableNSWindow Klasse auf true.
  • Dokumente in der Cloud – Vor macOS Sierra musste sich eine App explizit für die Arbeit mit Dokumenten im iCloud-Laufwerk des Benutzers anmelden. In macOS Sierra werden die Ordner "Desktop" und "Dokumente" des Benutzers möglicherweise automatisch mit ihrem iCloud-Laufwerk vom System synchronisiert. Daher können lokale Kopien von Dokumenten gelöscht werden, um Speicherplatz auf dem Computer des Benutzers freizugeben. NSDocument Basierende Apps behandeln diese Änderung automatisch. Alle anderen App-Typen müssen zum NSFileCoordinator Synchronisieren von Lese- und Schreibvorgängen von Dokumenten verwendet werden.

Zusammenfassung

Dieser Artikel enthält mehrere Tipps, Features und Techniken, die ein Entwickler zum Erstellen einer modernen macOS-App in Xamarin.Mac verwenden kann.