En este artículo, te guiaré paso a paso para crear una interfaz inspirada en el home de la app de Pinterest utilizando SwiftUI. Exploraremos componentes clave y cómo replicar un diseño moderno que es visualmente atractivo y funcional. Lo mejor de todo es que tardarás menos de 10 minutos en construir esta interfaz.
Estructura Inicial
Comenzaremos creando una estructura básica usando NavigationView
. Aunque en este ejemplo no implementaremos navegación entre vistas, es una buena práctica incluirla desde el principio, ya que facilita la expansión y el manejo del flujo de la aplicación en proyectos más complejos. Además, preparar el diseño con NavigationView
permite que la interfaz sea más flexible y escalable a futuro.
import SwiftUI
struct PinterestHomeView: View {
@State private var images = ["Pokemon-Setup", "Tloz-Tattoo", "Setup-Zelda", "Apple-Watch-Wallpaper", "Super-Mario", "Tokio"]
var body: some View {
NavigationView {
VStack(spacing: 0) {
// Aquí añadiremos nuestros componentes en los próximos pasos.
}
.navigationBarHidden(true)
}
}
}
@State private var images crea una propiedad @State llamada images, que contiene una lista de nombres de imágenes. En este caso, los nombres de las imágenes, como “Pokemon-Setup” y “Tloz-Tattoo”, son recursos específicos que utilizo para este ejemplo, y podrás encontrarlos en el repositorio que comparto al final del artículo. Sin embargo, siéntete libre de reemplazarlos con las imágenes que desees para personalizar tu propia versión de la interfaz.
Creación de la Barra de Categorías (CategoryTabView)
La versión más actual de Pinterest (a día de hoy) cuenta con una barra de categorías en la parte superior, que permite al usuario cambiar entre diferentes temas. Vamos a replicarla.
struct CategoryTabView: View {
@State private var selectedCategory = "Todas"
let categories = ["Todas", "Setup Ideas"]
@Binding var images: [String]
var body: some View {
ScrollView(.horizontal, showsIndicators: false) {
HStack(spacing: 20) {
ForEach(categories, id: \.self) { category in
VStack {
Text(category)
.font(.headline)
.fontWeight(selectedCategory == category ? .bold : .regular)
.padding(.vertical, 12)
.padding(.horizontal, 16)
.background(Color.clear)
// Subrayado de la categoría seleccionada
if selectedCategory == category {
Rectangle()
.fill(Color.black)
.frame(height: 2)
.cornerRadius(1)
} else {
Rectangle()
.fill(Color.clear)
.frame(height: 2)
}
}
.onTapGesture {
selectedCategory = category
// Cambia las imágenes según la categoría seleccionada
if category == "Setup Ideas" {
images = ["Pokemon-Setup", "Setup-Zelda"]
} else {
images = ["Pokemon-Setup", "Tloz-Tattoo", "Setup-Zelda", "Apple-Watch-Wallpaper", "Super-Mario", "Tokio"]
}
}
}
}
.padding(.horizontal)
}
.background(Color.white.shadow(radius: 2))
}
}
El estado de la categoría seleccionada se maneja con una propiedad @State llamada selectedCategory. La lista de categorías, en este caso “Todas” y “Setup Ideas”, se define como un array constante. Cada vez que el usuario selecciona una categoría, selectedCategory se actualiza, lo que a su vez cambia el estilo visual de la categoría seleccionada, haciéndola más prominente mediante un subrayado negro.
El componente ScrollView permite que la barra de categorías sea desplazable horizontalmente, facilitando la adición de más categorías en el futuro sin comprometer el diseño. Dentro de este scroll, HStack organiza las categorías en una fila con un espaciado fijo entre ellas.
Cada categoría se renderiza como un texto dentro de un VStack, que también incluye un rectángulo que actúa como subrayado para la categoría seleccionada. Si el usuario toca una categoría, un onTapGesture actualiza la categoría seleccionada y cambia las imágenes mostradas en la cuadrícula principal utilizando la propiedad @Binding images. Dependiendo de la categoría seleccionada, se actualiza la lista de imágenes para reflejar la selección del usuario, ofreciendo una experiencia interactiva y personalizada.
Agreguemos nuestra vista recién creada (CategoryTabView) a nuestra estructura principal:
var body: some View {
NavigationView {
VStack(spacing: 0) {
CategoryTabView(images: $images)
}
.navigationBarHidden(true)
}
}
Vista Previa:
Implementación de la Cuadrícula de Imágenes (DynamicGridView)
Lo más representativo de Pinterest es su diseño en cuadrícula, donde las imágenes tienen diferentes alturas pero están organizadas en dos columnas. Vamos a crear esta cuadrícula con imágenes de tamaños variables.
struct DynamicGridView: View {
@Binding var images: [String]
var body: some View {
let screenWidth = UIScreen.main.bounds.width
let itemWidth = (screenWidth / 2) - 24
ScrollView {
LazyVGrid(columns: [
GridItem(.fixed(itemWidth), spacing: 16),
GridItem(.fixed(itemWidth), spacing: 16)
], spacing: 16) {
ForEach(images, id: \.self) { image in
Image(image)
.resizable()
.scaledToFill()
.frame(width: itemWidth, height: CGFloat.random(in: 150...300))
.clipped()
.cornerRadius(8)
}
}
.padding(.horizontal)
.padding(.top, 16)
}
}
}
La lista de imágenes se maneja a través de una propiedad @Binding, lo que permite que la vista actualice su contenido en función de los cambios en la lista de imágenes que se le pasa desde una vista superior.
Primero, calculamos el ancho de cada ítem en la cuadrícula dividiendo el ancho de la pantalla en dos y restando un margen para el espaciado entre las columnas. Esto garantiza que las imágenes se ajusten uniformemente en una cuadrícula de dos columnas.
La cuadrícula en sí se construye utilizando LazyVGrid, que organiza las imágenes en dos columnas con un espaciado fijo entre ellas. Dentro de la cuadrícula, cada imagen se carga dinámicamente de la lista images, y se ajusta su tamaño para llenar su espacio asignado mientras mantiene su proporción. La altura de cada imagen varía aleatoriamente para crear un diseño de estilo “masonry”, similar al de Pinterest.
Finalmente, se aplican ajustes de estilo como el redondeo de esquinas y el recorte de las imágenes para asegurar un diseño moderno y atractivo. La cuadrícula completa está envuelta en un ScrollView, lo que permite que el usuario se desplace verticalmente por el contenido si hay más imágenes de las que caben en la pantalla.
Agreguemos nuestra vista recién creada (DynamicGridView) a nuestra estructura principal:
var body: some View {
NavigationView {
VStack(spacing: 0) {
CategoryTabView(images: $images)
DynamicGridView(images: $images)
}
.navigationBarHidden(true)
}
}
Vista Previa:
Finalizando con la Barra de Pestañas
Para completar la interfaz, agregaremos una barra de pestañas en la parte inferior, similar a muchas aplicaciones modernas.
struct CustomTabBarView: View {
var body: some View {
HStack {
Spacer()
Image(systemName: "house.fill")
Spacer()
Image(systemName: "magnifyingglass")
Spacer()
Image(systemName: "plus.app.fill")
Spacer()
Image(systemName: "bell")
Spacer()
Image(systemName: "person.crop.circle")
Spacer()
}
.padding()
.background(Color.white.shadow(radius: 2))
}
}
Para asegurar que los íconos estén uniformemente distribuidos a lo largo de la barra, se utilizan Spacer entre cada Image. Los Spacer crean espacio adicional, separando los íconos y centrándolos dentro de la barra, lo que proporciona un diseño limpio y equilibrado.
Agreguemos nuestra vista recién creada (CustomTabBarView) a nuestra estructura principal:
var body: some View {
NavigationView {
VStack(spacing: 0) {
CategoryTabView(images: $images)
DynamicGridView(images: $images)
CustomTabBarView()
}
.navigationBarHidden(true)
}
}
Vista Previa:
Conclusión
Así de sencillo es estructurar una aplicación utilizando SwiftUI. Hemos recreado una interfaz moderna inspirada en Pinterest, desglosando cada componente y construyéndolo paso a paso. Este tipo de proyectos no solo te permite comprender mejor el flujo de trabajo en SwiftUI, sino que también te ayuda a interiorizar conceptos clave de desarrollo de interfaces de manera práctica y efectiva.