Posts

Barras de herramientas en iPhone Duo: cómo adaptar SwiftUI a la disposición vertical

Arturo Rivas Arias

En iPhone Duo, los controles de navegación, las acciones de la barra de herramientas y las pestañas pueden compartir una franja vertical en el borde de la pantalla. Así queda más altura para el contenido, pero también cambia el espacio disponible para cada botón. Si construyes la app con el SDK de iOS 27.1 y utilizas contenedores estándar como NavigationStack, NavigationSplitView o TabView, SwiftUI se encarga de buena parte de la adaptación.

No hace falta colocar manualmente cada acción en el lateral. Sigues declarando ToolbarItem con sus ubicaciones habituales y el sistema decide cómo presentarlo según el contexto. Las nuevas API permiten intervenir cuando un control personalizado necesita un tratamiento especial.

Elegir el eje de un elemento

Un botón con un símbolo suele poder pasar a la barra vertical automáticamente. Los elementos que solo muestran texto o incluyen contenido complejo tienden a permanecer en una barra horizontal. axisBehavior(_:), aplicado al ToolbarItem, permite expresar una preferencia distinta.

Imagina una agenda con un menú compacto para elegir la vista. Su etiqueta combina un símbolo y un texto corto:

ToolbarItem(placement: .bottomBar) {
    Menu {
        Button("Día", systemImage: "calendar.day.timeline.left") {
            selectedView = .day
        }
        Button("Semana", systemImage: "calendar") {
            selectedView = .week
        }
    } label: {
        Label("Vista", systemImage: "calendar")
    }
}
.axisBehavior(.verticalPreferred)

Con .verticalPreferred, indicamos que ese menú puede participar en la disposición lateral. Conviene comprobar el resultado en el simulador: pedir el eje vertical no convierte una etiqueta ancha en un control legible por arte de magia. Para un selector que necesita anchura, como un Picker segmentado, .axisBehavior(.horizontalOnly) deja explícito que debe permanecer horizontal. También evita que cambie de eje si su contenido varía.

Ajustar el contenido personalizado al eje real

El valor de entorno toolbarVerticalEdge comunica el borde preferido para la barra vertical: leading, trailing o nil cuando ese contexto no utiliza barras verticales. Sirve para adaptar la etiqueta de un control sin deducir el diseño a partir del modelo del dispositivo.

struct VistaAgendaLabel: View {
    @Environment(\.toolbarVerticalEdge) private var verticalEdge

    var body: some View {
        let layout = verticalEdge == nil
            ? AnyLayout(HStackLayout(spacing: 4))
            : AnyLayout(VStackLayout(spacing: 4))

        layout {
            Image(systemName: "calendar")
            Text("Vista")
                .font(.caption)
        }
    }
}

Este enfoque permite mostrar símbolo y texto uno al lado del otro en una barra horizontal y apilarlos cuando la barra es vertical. Si el elemento está marcado como .horizontalOnly, su propia etiqueta debe conservar el diseño horizontal aunque toolbarVerticalEdge tenga un valor por la presencia de otras barras laterales.

Decidir qué se comprime primero

Al reunir pestañas y acciones en el mismo lateral, el espacio puede agotarse. SwiftUI puede enviar acciones a un menú de desbordamiento o reunir las pestañas en un único control. toolbarVerticalCompressionBehavior(_:) establece cuál de los dos grupos tiene prioridad.

TabView {
    Tab("Agenda", systemImage: "calendar") {
        NavigationStack {
            AgendaView()
                .toolbarVerticalCompressionBehavior(.prefersToolbarItems)
        }
    }

    Tab("Buscar", systemImage: "magnifyingglass") {
        BuscarEventosView()
    }
}

En esta pantalla, .prefersToolbarItems conserva durante más tiempo las acciones de la agenda y permite que se compacten antes las pestañas. Si la navegación entre secciones es prioritaria, .prefersTabBar invierte esa preferencia. El comportamiento automático de iOS favorece las pestañas: las acciones de la barra pasan antes al desbordamiento. Esta API decide entre grupos; para priorizar acciones concretas dentro de la barra se utiliza visibilityPriority(_:).

Cuándo conservar las barras horizontales

No todas las presentaciones aprovechan la disposición vertical. Una vista de ayuda con un único botón de cerrar puede perder anchura útil si ese botón ocupa un lateral. En ese caso, toolbarVerticalBehavior(_:) permite desactivar la barra vertical en la presentación:

.sheet(isPresented: $showsHelp) {
    AgendaHelpView()
        .toolbarVerticalBehavior(.disabled)
}

La vista configura sus barras de forma independiente de la pantalla que la presenta. Dentro de una misma pila de navegación conviene mantener este comportamiento estable: alternar entre barras horizontales y verticales en cada destino provoca cambios de disposición durante la navegación.

La estrategia práctica es empezar por los contenedores y controles estándar, probar las distintas posiciones y orientaciones del iPhone Duo y ajustar únicamente los elementos que lo necesiten. El eje, el espacio para pestañas y acciones, y la decisión de usar una barra vertical pertenecen a niveles distintos de la interfaz. SwiftUI ofrece una API específica para cada uno.