Cuando diseñamos apps para pantallas más grandes, como por ejemplo iPad, Mac o algunos iPhone en modo «landscape», se debe aprovechar el espacio de la pantalla de una manera eficiente y mostrar más información en ella.
Para ello, lo más habitual es sustituir el TabBar por una pantalla con dos (o tres) columnas:
Cada columna suele recibir un nombre, que varía en función de si la pantalla tiene dos o tres columnas.
Para aquellas con tres columnas:
Primary
: es la columna a la izquierda, la primera.Supplementary
: es la columna del medioSecondary
: es la columna a la derecha, la última.
En aquellas con solamente dos columnas, éstas reciben el nombre de Primary
(la primera) y Secondary
(la segunda).
En UIKit
esto se puede hacer utilizando un UISplitViewController
y utilizando delegados, pero… ¿Cómo se puede hacer en SwiftUI
?
Crear un «SplitViewController» en SwiftUI:
En SwiftUI, para crear una interfaz con dos o tres columnas se hace añadiendo una segunda (o tercera) vista a un NavigationView
.
Si queremos dos columnas:
struct ContentView: View {
var body: some View {
PrimaryView()
SecondaryView()
}
}
Si quieres que haya una tercera columna, basta con añadir otra vista más:
struct ContentView: View {
var body: some View {
PrimaryView()
SupplementaryView()
SecondaryView()
}
}
Creando la Primary
View: un sidebar en SwiftUI
Imagina que estamos creando la app de este blog, y queremos dividirla en tres columnas: categorías, posts pertenecientes a la categoría seleccionada, y por último el post seleccionado.
La primera columna debería ser, según las guías de estilo, una lista en formato sidebar. Para ello, basta con aplicarle el modificador .listStyle()
al componente List
de SwiftUI:
struct CategoSidebarrView: View {
let categories = CategoryRepository.all
var body: some View {
List(categories) { category in
// 1
NavigationLink(
destination: PostListView(posts: category.posts),
label: {
CategoryRow(category: category)
})
}
.listStyle(SidebarListStyle()) // 2
.navigationBarTitle("Categories")
}
}
// 1
: Creamos unNavigationLink
para navegar a la lista de artículos perteneciente a la categoría seleccionada// 2
: Aplicamos el modificador.listStyle()
al componente de la lista, pasándole el estilo deseado:SidebarListStyle()
Esto hará que, dependiendo de la plataforma y del tamaño disponible de la pantalla, SwiftUI aplique el estilo correspondiente para los sidebars.
Creando la Supplementary
y Secondary
View:
Según las guías de estilo, si la supplementary
view es una lista de elementos, el estilo ideal para esta vista es un PlainListStyle()
(es el estilo por defecto, así que no hay que especificarlo explícitamente). En este ejemplo la supplementary
view será una lista de Posts:
struct PostListView: View {
let posts: [Post]
var body: some View {
List(posts) { post in
NavigationLink(destination: URLView(url: post.url)) {
Image(systemName: "safari")
.resizable()
.frame(width: 20, height: 20)
Text(post.title)
.font(.body)
}
}
.listStyle(PlainListStyle())
.navigationBarTitle("Posts")
}
}
Por último, la secondary
view sería el propio artículo. Por simplificar el tutorial, simplemente mostraremos la URL del artículo sobre un color de fondo aleatorio:
struct URLView: View {
let url: URL
var body: some View {
Label(url.absoluteString, systemImage: "heart.fill")
.frame(maxWidth: .infinity, maxHeight: .infinity)
.foregroundColor(.white)
.font(.headline)
.padding(20)
.background(
Color(
red: .random(in: 0.5...1),
green: .random(in: 0.5...1),
blue: .random(in: 0.5...1)
)
)
.cornerRadius(8)
.padding(20)
.navigationBarTitle("URL")
}
}
Combinando las tres vistas en un SidebarNavigation
View:
Por último, es momento de crear la vista de la navegación para pantallas grandes. Crea un nuevo fichero llamado SidebarNavigation
:
struct SidebarNavigation: View {
var body: some View {
NavigationView {
CategorySidebarView() // 1
PostListView(posts: PostRepository.uiKitPosts) // 2
URLView(url: PostRepository.uiKitPosts[0].url) // 3
}
}
}
// 1
: AsignamosCategorySidebarView
como la columnaprimary
.// 2
: AsignamosPostListView
como la columnasupplementary
. Al iniciar la app, se verá la lista de post de la categoría de UIKit.// 3
: AsignamosURLView
como la columnasecondary
. Al iniciar la app, se verá la URL del primer post de UIKit.
Cambiar la vista principal de la app:
Abre la vista ContentView
y utiliza el SidebarNavigation
:
struct ContentView: View {
var body: some View {
SidebarNavigation()
}
}
Ejecuta la en un simulador de iPad y verás las diferentes columnas:
Mejorando la usabilidad de la app:
La guía de estilo dice que en tamaños más pequeños de pantalla, por ejemplo iPhones en portrait o incluso cuando se utiliza el iPad en pantalla dividida, debemos sustituir la navegación de columnas por una navegación de tipo TabBar.
¿Cómo podemos hacer esto en SwiftUI?
Paso 1: Crear un TabNavigation
View:
En este caso, vamos a sustituir la navegación en formato columna de las categorías y los posts por una navegación en formato TabBar. Para ello, crea un nuevo fichero TabNavigation
:
struct TabNavigation: View {
var body: some View {
TabView {
// Categories tab
NavigationView {
CategorySidebarView()
}
.tabItem {
Image(systemName: "list.dash")
Text("Categories")
}
// Posts tab
NavigationView {
PostListView(posts: PostRepository.uiKitPosts)
}
.tabItem {
Image(systemName: "pencil.slash")
Text("Post")
}
}
}
}
Paso 2: Cambiar el tipo de navegación cuando la pantalla sea más pequeña:
No se debe de diseñar la interfaz de la app basándose en si se va a utilizar en un iPad o en un iPhone, si no en el tamaño de pantalla disponible. Recuerda, el tamaño de pantalla disponible de una aplicación en un iPad puede cambiar cuando se utiliza en pantalla dividida.
Por ello, lo ideal es diseñarlo según el tamaño de pantalla disponible. Para poder «leer» el tamaño de pantalla, los desarrolladores utilizamos las SizeClasses. Existen dos tipos de SizeClasses:
regular
para referirse al tamaño de pantalla «grande», con espacio suficiente para mostrar más información en la pantalla.compact
: para referirse al tamaño de pantalla «pequeño», aquel que no permite mostrar tanta información en la pantalla.
Para implementar esto en la app, tenemos que añadir una propiedad horizontalSizeClass
a la vista ContentView
:
struct ContentView: View {
#if os(iOS)
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
#endif
var body: some View {
SidebarNavigation()
}
}
En macOS, no tenemos SizeClasses, así que utilizamos la macro #if os(iOS)
para añadir esta propiedad solamente cuando se trate de aplicaciones iOS o iPadOS.
Ahora modificamos la propiedad body
para comprobar el valor de esta propiedad, y devolver TabNavigation
o SidebarNavigation
cuando corresponda:
struct ContentView: View {
#if os(iOS)
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
#endif
var body: some View {
#if os(iOS)
if horizontalSizeClass == .compact {
return TabNavigation()
} else {
return SidebarNavigation()
}
#else // macOS
return SidebarView()
#endif
}
}
Por último, haremos uso de otro PropertyWrapper para evitar tener que utilizar la keyword return
en SwiftUI: @ViewBuilder
struct ContentView: View {
#if os(iOS)
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
#endif
@ViewBuilder var body: some View {
#if os(iOS)
if horizontalSizeClass == .compact {
TabNavigation()
} else {
SidebarNavigation()
}
#else
SidebarView()
#endif
}
}
Descarga GRATIS la chuleta de atajos de Xcode
He creado una chuleta en PDF con los 35 shortcuts de Xcode que todo iOS developer debe conocer. Tenlos todos juntos y a mano para poder consultarlos en cualquier momento 🙂
Déjame tus datos y te la enviaré a tu correo electrónico.
Conclusión
Nunca había sido tan fácil crear aplicaciones universales, tanto para iOS, iPadOs y ahora también macOS. Está claro que SwiftUI es el futuro de la creación de interfaces para sistemas Apple, pero UIKit seguirá vivo durante muchos años todavía.
¿Te gustaría que escribiese el mismo tutorial pero utilizando UIKit?
¿Qué te ha parecido el artículo? Déjame tu feedback en los comentarios y lo leeré encantado 😃
👇👇👇