Esta página foi traduzida automaticamente e pode conter erros. Ler o original em inglês

Documentação

Código específico de plataforma

Um único bundle NSH roda em iOS, Android, macOS, Windows e Linux (e no Mac Catalyst), e em navegadores por meio do runtime web. A maior parte de um app não precisa de nenhuma verificação de plataforma: elementos, estilos, layout e módulos se comportam da mesma forma em todo lugar, e os componentes que você cria a partir deles têm a mesma aparência em todo lugar. Quando algo realmente é específico de uma plataforma, o NSH oferece um lugar claro para isso, de modo que o código de plataforma fique visível e contido:

Rust TSX Para
Métodos builder (.ripple(..), .input_accessory(..)) ios={{…}}, android={{…}}, desktop={{…}}, web={{…}} Props que só uma plataforma tem.
on_back_press, nsh_core::element::desktop nsh/mobile, nsh/desktop APIs que só as plataformas mobile ou só os desktops têm.
os(), idiom(), is_desktop() platform.os, platform.desktop, platform.select Escolha de valores ou de código por plataforma.
.when(os() == Os::Ios, ..) classes ios: / android: / web: Estilos por plataforma.

#Desktop e mobile

Toda plataforma é desktop ou mobile, segundo uma única regra. Desktop é macOS, Windows, Linux e navegadores no idioma desktop (mouse e uma janela larga, a web). Mobile é iOS, Android e navegadores no idioma de celular ou tablet; o Mac Catalyst executa o backend do iOS, então também é mobile, com props ios={{…}}. As props desktop={{…}}, platform.select({ desktop, mobile }), platform.desktop e platform.idiom === "desktop" seguem essa regra, assim como os componentes de nsh/base (um menu é um popup nos desktops e uma sheet no mobile) e, em Rust, is_desktop().

O isAvailable de nsh/desktop responde a uma pergunta mais restrita: se as APIs de janela e de menu desse módulo funcionam, o que acontece no macOS, no Windows e no Linux. Condicione layout, densidade e entrada a platform.desktop, e chamadas de Window e Menu a isAvailable.

#Props exclusivas de plataforma

Em Rust, uma prop que só uma plataforma tem é um método builder como outro qualquer (.ripple(..), .elevation(..) para Android, .input_accessory(..) para iOS, .window_drag(..) para os desktops); as outras plataformas a ignoram. Em TSX, as props que existem em apenas uma plataforma ficam agrupadas em um objeto com o nome dela. Cada elemento aceita ios, android, desktop (macOS, Windows, Linux e navegadores no idioma desktop) e web (navegadores); nas outras plataformas o objeto é ignorado, e na plataforma correspondente apenas as suas chaves são aplicadas. Um navegador desktop aplica desktop e web.

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(…)
})

Os objetos TSX são reativos como qualquer prop; uma chave removida do objeto é limpa.

O que existe hoje:

Rust TSX Em Descrição
.ripple(Ripple { .. }) android={{ ripple }} todos os elementos O ripple do material enquanto pressionado: cor, sem borda, raio, primeiro plano.
.elevation(..) android={{ elevation }} todos os elementos A sombra do material, em pontos, a partir da forma da view.
android={{ accessibilityLiveRegion }} todos os elementos "none", "polite" ou "assertive": o TalkBack anuncia as mudanças da view.
.status_bar_translucent(..) android={{ statusBarTranslucent }} Dialog O conteúdo do diálogo se estende por baixo da barra de status.
.input_accessory(..) ios={{ inputAccessoryViewID }} Input O InputAccessoryView exibido sobre o teclado.
.auto_fill(..) ios={{ textContentType }} Input Os nomes do iOS para os tipos de preenchimento automático (o AutoFill do Rust cobre as duas plataformas).
.supported_orientations(..) ios={{ supportedOrientations }} Dialog As orientações que ele permite.
.back_forward_gestures(..) ios={{ allowsBackForwardNavigationGestures }} WebView Deslizar para voltar e avançar no histórico de páginas.
.header_right_system_item(..) ios={{ headerRightSystemItem }} NavPage O botão de barra nativo do UIKit no fim do cabeçalho, que ao ser pressionado aciona headerRight.
.header_left_system_item(..) ios={{ headerLeftSystemItem }} NavPage O botão de barra nativo do UIKit no início do cabeçalho, que ao ser pressionado aciona headerLeft.
.header_right_symbol(..) ios={{ headerRightSymbol }} NavPage O botão de barra nativo do UIKit exibindo um SF Symbol no fim do cabeçalho, que ao ser pressionado aciona headerRight.
.header_left_symbol(..) ios={{ headerLeftSymbol }} NavPage O botão de barra nativo do UIKit exibindo um SF Symbol no início do cabeçalho, que ao ser pressionado aciona headerLeft.
.back_forward_gestures(..) desktop={{ allowsBackForwardNavigationGestures }} WebView macOS: deslizes de dois dedos no trackpad voltam e avançam no histórico de páginas; Windows: deslizes na tela sensível ao toque; Linux: deslizes no touchpad.
.window_drag(..) desktop={{ windowDrag }} todos os elementos Pressioná-lo arrasta a janela, como faria a barra de título do sistema (veja nsh/desktop).

Em TSX, desktop={{…}} e web={{…}} são aceitos em qualquer lugar. Os tipos (IOSProps, AndroidProps, DesktopProps e suas formas por elemento) informam ao seu editor o que cada elemento aceita.

O que um elemento significa em todas as plataformas é uma prop de nível superior (em Rust, um método com o mesmo nome), aplicada onde faz sentido: title, headerStyle, headerLargeTitle e headerBackTitle de NavPage configuram o cabeçalho do sistema no iOS e no Android, o slot header dele é o cabeçalho dos desktops e da web, e presentation de Dialog escolhe as sheets do iOS. closeOnBackdropPress, pagingEnabled, refreshing, hitSlop e o restante de AlertDialog.show funcionam em todas as plataformas. Um recurso de sistema próprio de uma plataforma vai no objeto dela e, quando várias plataformas o têm, no de cada uma delas com o mesmo nome: allowsBackForwardNavigationGestures de WebView, os deslizes do sistema pelo histórico de páginas, está em ios={{…}} e desktop={{…}}.

#Módulos de plataforma

Em Rust, as APIs que só uma plataforma tem são funções de nsh_core::prelude (on_back_press, input_accessory_view) e de nsh_core::element::desktop (a janela: set_title, set_title_bar, title_bar, minimize, toggle_maximize, toggle_full_screen, set_frame, set_level, order_front, close_window; menus: set_application_menu, popup_menu); nas outras plataformas elas não fazem nada. Em TSX, nsh/mobile reúne as APIs do iOS e do Android (algumas de apenas uma das plataformas) e nsh/desktop as dos desktops. Ambos podem ser importados em qualquer plataforma, então um único bundle roda em todo lugar: nas outras plataformas, suas funções retornam imediatamente e seus componentes não ocupam espaço. Cada um exporta isAvailable, que é true nas suas próprias plataformas; platform.os distingue um do outro.

#nsh/mobile

isAvailable é true no iOS (com Mac Catalyst) e no Android. Ambas as plataformas têm TabBar, a barra de abas do sistema.

#BackHandler (Android)

Rust TSX Descrição
on_back_press(listener) BackHandler.on("press", listener) Escuta o botão voltar depois dos diálogos e antes das pilhas; retorne true quando tratado. O listener acompanha o componente (ou effect) que o adicionou; em TSX, a chamada também retorna uma função que o interrompe antes.
BackHandler.exitApp() Sai do 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
    });
    // …
}

O botão voltar é tratado primeiro pelo diálogo do topo, depois pelos listeners de voltar (do mais recente ao mais antigo), depois pela pilha de navegação do topo, e o app é encerrado quando nada o tratou. Veja Navegação.

#InputAccessoryView (iOS)

Rust TSX Descrição
input_accessory_view(native_id) InputAccessoryView Uma barra sobre o teclado dos inputs que citam o seu id nativo (.input_accessory(..), ios={{ inputAccessoryViewID }}).

Veja Formulários e o teclado.

#nsh/desktop

Apps Rust desenham suas próprias barras de título com nsh_core::element::desktop (Building with Rust). O módulo do TSX:

Export Descrição
Window A janela: setTitle, setTitleBar({ style: "hidden" }) para uma barra de título própria do app, minimize, toggleMaximize, toggleFullScreen, close.
titleBar A altura da barra de título oculta e o espaço ocupado pelos controles de janela do sistema (reativo).
Menu setApplicationMenu(items) (a barra de menus, com atalhos) e popup(items, at?) (menus de contexto).
isAvailable true onde as APIs de janela e de menu funcionam: macOS, Windows e Linux. Navegadores desktop têm platform.desktop e mantêm a janela e os menus do navegador.

Veja nsh/desktop.

#A barra de status

Em ambas as plataformas, a barra de status pertence à página ou ao diálogo do topo, como no UIKit, onde o view controller mais acima decide isso: NavPage e Dialog aceitam statusBar (em Rust, .status_bar(StatusBarStyle::Light), ou um StatusBarSetting para os demais campos) com foreground ("auto", "light" para fundos escuros, "dark" para fundos claros), hidden e animated. Vence a página ou o diálogo exibido mais recentemente, campo a campo, e o que está abaixo dele volta a valer quando ele sai. "statusBar" de nsh.json é o padrão do app por baixo deles.

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

Em um app embutido em um app nativo, o view controller do app host decide a barra de status no iOS. Um app que declara "capabilities" em nsh.json precisa de "statusBar": true para alterá-la.

#platform.os e platform.select

os() em Rust e a constante platform de nsh em TSX informam onde o app está rodando: Os::Ios, Os::Android, Os::MacOs, Os::Windows, Os::Linux ou Os::Web, e platform.os "ios", "android", "macos", "windows", "linux" ou "web" (o Mac Catalyst é "ios", com platform.catalyst em qualquer um dos seus idiomas; o backend macOS é "macos", o backend Linux é "linux" e o backend Windows é "windows", e seus elementos aplicam suas props desktop; os navegadores são "web" e também aplicam props desktop quando platform.desktop é true). Veja Dispositivo e ambiente para a versão, o idioma e o restante.

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

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

#platform.select

Em Rust, um match os() escolhe o valor, com is_desktop() para os desktops. Em TSX, platform.select(spec) retorna o valor para esta plataforma. Ele procura a chave do próprio SO, depois desktop ou mobile conforme a regra acima (platform.desktop), depois native (todas as plataformas, exceto a web) e, por fim, 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",
};

Ele retorna undefined quando nada corresponde e não há default.

#Renderizando componentes diferentes

Escolha componentes da mesma forma, no nível do módulo (TSX) quando a escolha nunca muda:

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

ou inline com when / <If>:

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

#Variantes de plataforma no Tailwind

Em Rust, estilize por plataforma com .when(os() == Os::Ios, |v| ..); em TSX, as variantes ios:, android: e web: aplicam classes em uma única plataforma:

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

#Prefira comportamento compartilhado

Antes de recorrer a uma verificação de plataforma, considere se a diferença importa para os seus usuários. Elementos e estilos já mapeiam para o comportamento nativo de cada plataforma onde ele deve diferir (física de rolagem, renderização de texto, teclados, acessibilidade), e permanecem iguais onde os usuários esperam consistência (a aparência própria do seu app). As verificações de plataforma são mais bem reservadas para convenções que os usuários esperam: o botão voltar e os toasts do Android, as sheets e os títulos grandes do iOS, os nomes de preenchimento automático, o ripple do Android.

#Relacionados