Diese Seite wurde maschinell übersetzt und kann Fehler enthalten. Das englische Original lesen

Dokumentation

Plattformspezifischer Code

Ein NSH-Bundle läuft auf iOS, Android, macOS, Windows und Linux (und in Mac Catalyst) sowie in Browsern über die Web-Runtime. Die meisten Teile einer App kommen ganz ohne Plattformprüfungen aus: Elemente, Styles, Layout und Module verhalten sich überall gleich, und die daraus gebauten Komponenten sehen überall gleich aus. Wenn etwas wirklich plattformspezifisch ist, gibt NSH dafür einen klaren Platz, damit Plattformcode sichtbar und abgegrenzt bleibt:

Rust TSX Für
Builder-Methoden (.ripple(..), .input_accessory(..)) ios={{…}}, android={{…}}, desktop={{…}}, web={{…}} Props, die nur eine Plattform hat.
on_back_press, nsh_core::element::desktop nsh/mobile, nsh/desktop APIs, die nur mobile Plattformen oder nur Desktops haben.
os(), idiom(), is_desktop() platform.os, platform.desktop, platform.select Werte oder Code pro Plattform auswählen.
.when(os() == Os::Ios, ..) Klassen ios: / android: / web: Styles pro Plattform.

#Desktop und Mobile

Jede Plattform ist nach einer einzigen Regel entweder Desktop oder Mobile. Desktop sind macOS, Windows, Linux und Browser im Desktop-Idiom (eine Maus und ein breites Fenster, das Web). Mobile sind iOS, Android und Browser im Telefon- oder Tablet-Idiom; Mac Catalyst nutzt das iOS-Backend und ist daher ebenfalls Mobile, mit ios={{…}}-Props. desktop={{…}}-Props, platform.select({ desktop, mobile }), platform.desktop und platform.idiom === "desktop" folgen alle dieser Regel, ebenso die Komponenten von nsh/base (ein Menü ist auf Desktops ein Popup und auf Mobilgeräten ein Sheet) und in Rust is_desktop().

isAvailable von nsh/desktop beantwortet eine engere Frage: ob die Fenster- und Menü-APIs dieses Moduls funktionieren, was unter macOS, Windows und Linux der Fall ist. Steuere Layout, Dichte und Eingabe über platform.desktop, und Aufrufe von Window und Menu über isAvailable.

#Plattformspezifische Props

In Rust ist ein Prop, den nur eine Plattform hat, eine Builder-Methode wie jede andere (.ripple(..), .elevation(..) für Android, .input_accessory(..) für iOS, .window_drag(..) für die Desktops); die anderen Plattformen ignorieren ihn. In TSX sind Props, die nur auf einer Plattform existieren, in einem nach ihr benannten Objekt gruppiert. Jedes Element akzeptiert ios, android, desktop (macOS, Windows, Linux und Browser im Desktop-Idiom) und web (Browser); auf anderen Plattformen wird das Objekt ignoriert, und auf der eigenen Plattform werden nur seine Schlüssel angewendet. Ein Desktop-Browser wendet sowohl desktop als auch web an.

use nsh_base::dialog;
use nsh_core::{
    Ripple,
    element::{AutoFill, Orientation}, prelude::*,
};

view()
    .on_press(open)
    .active(|s| s.opacity(0.6))
    .ripple(Ripple {
        color: Some(rgba(0x0000001f).into()),
        ..Ripple::default()
    })
    .elevation(px(4.0))
    .p_4()
    .rounded(px(12.0))
    .bg(white())
    .child(text("Open"))

input().placeholder("Code").auto_fill(AutoFill::OneTimeCode)

when(shown, || {
    dialog()
        .supported_orientations([Orientation::Portrait])
        .status_bar_translucent(true)
        .child(…)
})

Die TSX-Objekte sind wie jedes Prop reaktiv; ein aus dem Objekt entfernter Schlüssel wird zurückgesetzt.

Was es heute gibt:

Rust TSX Auf Beschreibung
.ripple(Ripple { .. }) android={{ ripple }} jedes Element Der Material-Ripple beim Drücken: Farbe, borderless, Radius, Vordergrund.
.elevation(..) android={{ elevation }} jedes Element Der Material-Schatten in Punkt, aus der Form der View.
android={{ accessibilityLiveRegion }} jedes Element "none", "polite" oder "assertive": TalkBack kündigt Änderungen der View an.
.status_bar_translucent(..) android={{ statusBarTranslucent }} Dialog Der Inhalt des Dialogs reicht unter die Statusleiste.
.input_accessory(..) ios={{ inputAccessoryViewID }} Input Die InputAccessoryView, die über der Tastatur angezeigt wird.
.auto_fill(..) ios={{ textContentType }} Input Die iOS-Namen für Autofill-Typen (AutoFill in Rust deckt beide Plattformen ab).
.supported_orientations(..) ios={{ supportedOrientations }} Dialog Die erlaubten Ausrichtungen.
.back_forward_gestures(..) ios={{ allowsBackForwardNavigationGestures }} WebView Wischen vor und zurück im Seitenverlauf.
.header_right_system_item(..) ios={{ headerRightSystemItem }} NavPage Der eigene Bar-Button von UIKit am Ende des Headers, der headerRight auslöst.
.header_left_system_item(..) ios={{ headerLeftSystemItem }} NavPage Der eigene Bar-Button von UIKit am Anfang des Headers, der headerLeft auslöst.
.header_right_symbol(..) ios={{ headerRightSymbol }} NavPage Der eigene Bar-Button von UIKit mit einem SF Symbol am Ende des Headers, der headerRight auslöst.
.header_left_symbol(..) ios={{ headerLeftSymbol }} NavPage Der eigene Bar-Button von UIKit mit einem SF Symbol am Anfang des Headers, der headerLeft auslöst.
.back_forward_gestures(..) desktop={{ allowsBackForwardNavigationGestures }} WebView macOS: Zwei-Finger-Wischen auf dem Trackpad geht im Seitenverlauf vor und zurück; Windows: Wischen auf dem Touchscreen; Linux: Wischen auf dem Touchpad.
.window_drag(..) desktop={{ windowDrag }} jedes Element Beim Drücken wird das Fenster gezogen, wie es die Titelleiste des Systems täte (siehe nsh/desktop).

In TSX werden desktop={{…}} und web={{…}} überall akzeptiert. Die Typen (IOSProps, AndroidProps, DesktopProps und ihre Formen pro Element) zeigen deinem Editor, was jedes Element annimmt.

Was ein Element auf jeder Plattform bedeutet, ist ein Prop auf oberster Ebene (in Rust eine Methode gleichen Namens) und wird dort angewendet, wo es sinnvoll ist: title, headerStyle, headerLargeTitle und headerBackTitle von NavPage richten den System-Header von iOS und Android ein, sein Slot header ist der Header der Desktops und des Webs, und presentation von Dialog wählt die Sheets von iOS. closeOnBackdropPress, pagingEnabled, refreshing, hitSlop und die übrigen von AlertDialog.show funktionieren auf jeder Plattform. Ein systemeigenes Feature einer Plattform steht in deren Objekt, und wenn mehrere Plattformen es haben, in jedem von ihnen unter demselben Namen: allowsBackForwardNavigationGestures von WebView, die Wischgesten des Systems durch den Seitenverlauf, steht in ios={{…}} und desktop={{…}}.

#Plattformmodule

In Rust sind die APIs, die nur eine Plattform hat, Funktionen von nsh_core::prelude (on_back_press, input_accessory_view) und von nsh_core::element::desktop (das Fenster: set_title, set_title_bar, title_bar, minimize, toggle_maximize, toggle_full_screen, set_frame, set_level, order_front, close_window; Menüs: set_application_menu, popup_menu); anderswo tun sie nichts. In TSX enthält nsh/mobile die APIs von iOS und Android (einige davon nur für eine der beiden Plattformen), nsh/desktop die der Desktops. Beide lassen sich auf jeder Plattform importieren, sodass ein Bundle überall läuft: Auf anderen Plattformen kehren ihre Funktionen sofort zurück, und ihre Komponenten belegen keinen Platz. Jedes exportiert isAvailable, das auf den eigenen Plattformen true ist; platform.os unterscheidet sie.

#nsh/mobile

isAvailable ist true auf iOS (mit Mac Catalyst) und Android. Beide Plattformen haben TabBar, die Tab-Leiste des Systems.

#BackHandler (Android)

Rust TSX Beschreibung
on_back_press(listener) BackHandler.on("press", listener) Lauscht auf die Zurück-Taste nach Dialogen und vor Stacks; gib true zurück, wenn sie behandelt wurde. Der Listener gehört zu der Komponente (oder dem Effekt), die ihn hinzugefügt hat; in TSX gibt der Aufruf außerdem eine Funktion zurück, die ihn früher beendet.
BackHandler.exitApp() Beendet die App.
use nsh_core::prelude::*;

fn player() -> impl IntoElement {
    let full_screen = signal(false);
    // Back leaves full screen before it leaves the page. The listener goes
    // with the view.
    on_back_press(move || {
        if !full_screen.get_untracked() {
            return false;
        }
        full_screen.set(false);
        true
    });
    // …
}

Die Zurück-Taste wird zuerst vom obersten Dialog behandelt, dann von den Back-Listenern (der jüngste zuerst), dann vom obersten Navigations-Stack; die App wird beendet, wenn niemand sie behandelt hat. Siehe Navigation.

#InputAccessoryView (iOS)

Rust TSX Beschreibung
input_accessory_view(native_id) InputAccessoryView Eine Leiste über der Tastatur der Eingaben, die ihre native ID nennen (.input_accessory(..), ios={{ inputAccessoryViewID }}).

Siehe Formulare und die Tastatur.

#nsh/desktop

Rust-Apps zeichnen ihre eigenen Titelleisten mit nsh_core::element::desktop (Mit Rust entwickeln). Das Modul für TSX:

Export Beschreibung
Window Das Fenster: setTitle, setTitleBar({ style: "hidden" }) für eine eigene Titelleiste der App, minimize, toggleMaximize, toggleFullScreen, close.
titleBar Die Höhe der ausgeblendeten Titelleiste und der Platz, den die Fenstersteuerung des Systems einnimmt (reaktiv).
Menu setApplicationMenu(items) (die Menüleiste, mit Tastenkürzeln) und popup(items, at?) (Kontextmenüs).
isAvailable true dort, wo die Fenster- und Menü-APIs funktionieren: macOS, Windows und Linux. Desktop-Browser haben platform.desktop und behalten das Fenster und die Menüs des Browsers.

Siehe nsh/desktop.

#Die Statusleiste

Auf beiden Plattformen gehört die Statusleiste der obersten Seite oder dem obersten Dialog, wie in UIKit, wo der oberste View Controller sie bestimmt: NavPage und Dialog akzeptieren statusBar (in Rust .status_bar(StatusBarStyle::Light) oder ein StatusBarSetting für die übrigen Felder) mit foreground ("auto", "light" für dunkle Hintergründe, "dark" für helle), hidden und animated. Die zuletzt angezeigte Seite oder der zuletzt angezeigte Dialog gewinnt, Feld für Feld, und die darunterliegende gilt wieder, sobald sie verschwindet. "statusBar" von nsh.json ist der Standard der App darunter.

use nsh_base::nav_page;
use nsh_core::prelude::*;

fn photo_viewer() -> impl IntoElement {
    nav_page()
        .header_visible(false)
        .status_bar(StatusBarStyle::Light)
        .child(view().flex_1().bg(black()))
}

In einer App, die in eine native App eingebettet ist, bestimmt unter iOS der View Controller der Host-App die Statusleiste. Eine App, die "capabilities" in nsh.json deklariert, braucht "statusBar": true, um sie zu ändern.

#platform.os und platform.select

os() in Rust und die Konstante platform aus nsh in TSX verraten dir, wo die App läuft: Os::Ios, Os::Android, Os::MacOs, Os::Windows, Os::Linux oder Os::Web, und platform.os "ios", "android", "macos", "windows", "linux" oder "web" (Mac Catalyst ist "ios", mit platform.catalyst in beiden Idiomen; das macOS-Backend ist "macos", das Linux-Backend "linux" und das Windows-Backend "windows", und ihre Elemente wenden ihre desktop-Props an; Browser sind "web" und wenden ebenfalls desktop-Props an, wenn platform.desktop true ist). Siehe Gerät und Umgebung für die Version, das Idiom und den Rest.

use nsh_core::{element::Os, prelude::*};

if os() == Os::Android {
    // …
}

#platform.select

In Rust wählt ein match os() den Wert aus, mit is_desktop() für die Desktops. In TSX gibt platform.select(spec) den Wert für diese Plattform zurück. Es sucht nach dem eigenen Schlüssel des Betriebssystems, dann nach desktop oder mobile gemäß der obigen Regel (platform.desktop), dann nach native (jede Plattform außer dem Web), dann nach default:

use nsh_core::{element::Os, prelude::*};

fn card() -> View {
    let card = view().rounded(px(12.0)).bg(white());
    match os() {
        Os::Ios => card.shadow_md(),
        Os::Android => card.border_1().border_color(rgb(0xe5e7eb)),
        _ => card,
    }
}

let hint = match os() {
    Os::Ios => "Swipe down to close",
    Os::Android => "Press back to close",
    _ => "Close",
};

Es gibt undefined zurück, wenn nichts passt und es kein default gibt.

#Unterschiedliche Komponenten rendern

Wähle Komponenten auf dieselbe Weise aus, auf Modulebene (TSX), wenn sich die Wahl nie ändert:

fn spinner() -> AnyElement {
    if os() == Os::Ios {
        ios_spinner().into_element()
    } else {
        material_spinner().into_element()
    }
}

oder inline mit when / <If>:

when(os() == Os::Ios, || input_accessory_view("bar").child(…))
    .otherwise(|| text("Android has no input accessories."))

#Plattformvarianten in Tailwind

Rust gestaltet pro Plattform mit .when(os() == Os::Ios, |v| ..); in TSX wenden die Varianten ios:, android: und web: Klassen auf einer Plattform an:

view()
    .rounded_xl()
    .bg(white())
    .p_4()
    .when(os() == Os::Ios, |v| v.shadow_md())
    .when(os() == Os::Android, |v| v.border_1().border_color(rgb(0xe4e4e7)))

#Gemeinsames Verhalten bevorzugen

Bevor du zu einer Plattformprüfung greifst, überlege, ob der Unterschied für deine Nutzer eine Rolle spielt. Elemente und Styles bilden bereits dort das native Verhalten jeder Plattform ab, wo es sich unterscheiden sollte (Scroll-Physik, Textdarstellung, Tastaturen, Barrierefreiheit), und bleiben gleich, wo Nutzer Konsistenz erwarten (das eigene Erscheinungsbild deiner App). Plattformprüfungen sind am besten für Plattformkonventionen reserviert, auf die sich Nutzer verlassen: die Zurück-Taste und Toasts von Android, Sheets und große Titel von iOS, Autofill-Namen, der Ripple von Android.

#Siehe auch