Entiendo perfectamente esa sensación: estás probando una aplicación, ves un número de seguimiento, una dirección o un código de descuento larguísimo, mantienes el dedo pulsado sobre la pantalla esperando copiarlo y… no ocurre nada. Como usuarios, es frustrante. Como creadores, es un detalle de experiencia de usuario (UX) que no podemos pasar por alto.
Si eres un iOS Developer, sabes que en los primeros días de la creación de interfaces declarativas con Apple, permitir que el usuario copiara un texto estático requería soluciones un tanto extrañas, como usar un UITextView deshabilitado o envolver componentes de UIKit. Afortunadamente, la programación Swift evoluciona rápidamente. Hoy vamos a sumergirnos en una de las herramientas más sencillas pero poderosas que tenemos a nuestra disposición: Text Selection en SwiftUI.
En este tutorial exhaustivo, aprenderás qué es, cómo implementarlo y cómo dominarlo en tus proyectos de Swift, creando aplicaciones universales para iOS, macOS y watchOS utilizando Xcode.
La Importancia de la Selección de Texto en la programación Swift
En el ecosistema moderno de desarrollo de Apple, SwiftUI ha cambiado las reglas del juego. Nos permite escribir código una vez y desplegarlo en múltiples plataformas. Sin embargo, cada plataforma tiene sus propias convenciones de interfaz de usuario. En iOS, los usuarios esperan poder hacer una pulsación larga (long press) para seleccionar texto; en macOS, esperan hacer clic y arrastrar el cursor; en watchOS, las interacciones son mucho más limitadas debido al tamaño de la pantalla.
Implementar Text Selection en SwiftUI de manera correcta asegura que tu aplicación se sienta nativa en todos estos dispositivos, elevando tu perfil como iOS Developer y mejorando drásticamente la retención y satisfacción de tus usuarios.
¿Qué es Text Selection en SwiftUI?
Introducido en iOS 15, macOS 12 y watchOS 8, el modificador .textSelection es una API declarativa que permite a los desarrolladores indicar al sistema si el texto dentro de una vista puede ser seleccionado por el usuario.
En lugar de tener que recurrir a componentes complejos o interactivos por defecto (como TextField o TextEditor), este modificador se aplica directamente a vistas estáticas como Text. Esto significa que puedes mostrar información de solo lectura (como un artículo de un blog, un recibo de compra o un registro de errores) y permitir que el usuario interactúe con el texto nativamente para copiarlo, buscarlo en el diccionario o compartirlo, todo con una sola línea de código en Swift.
Configurando el Entorno en Xcode
Antes de empezar a tirar líneas de código, vamos a preparar nuestro entorno de trabajo. Para este tutorial, vamos a simular que estamos construyendo “SnippetSaver”, una aplicación multiplataforma para guardar fragmentos de código y notas técnicas.
- Abre Xcode.
- Selecciona Create a new Xcode project.
- En la pestaña Multiplatform, elige App y haz clic en Next.
- Nombra tu proyecto (por ejemplo,
SnippetSaver). - Asegúrate de que la interfaz esté configurada en SwiftUI y el lenguaje en Swift.
- Guarda el proyecto en tu directorio de preferencia.
Al elegir una plantilla multiplataforma, Xcode configura automáticamente los targets para iOS y macOS (y puedes añadir watchOS fácilmente). Esto es ideal para ver cómo se comporta Text Selection en SwiftUI en diferentes entornos simultáneamente.
Implementación Básica: Seleccionando un Text View
Vamos a empezar con el caso de uso más básico. Tienes una vista Text simple y quieres que el usuario pueda copiar su contenido.
Abre tu archivo ContentView.swift y modifica el código para que se vea así:
import SwiftUI
struct ContentView: View {
var body: some View {
VStack(spacing: 20) {
Image(systemName: "doc.text.magnifyingglass")
.imageScale(.large)
.foregroundColor(.blue)
Text("No puedes copiar este texto.")
.font(.headline)
.foregroundColor(.secondary)
// Aquí aplicamos Text Selection en SwiftUI
Text("¡Pero sí puedes copiar este fragmento de código! let text = \"SwiftUI es genial\"")
.font(.system(.body, design: .monospaced))
.padding()
.background(Color.gray.opacity(0.1))
.cornerRadius(8)
.textSelection(.enabled) // <- La magia ocurre aquí
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Análisis del Código Swift
Text("No puedes copiar este texto."): Este texto tiene el comportamiento predeterminado. Si lo ejecutas en el simulador de iOS o en tu iPhone, por mucho que mantengas el dedo pulsado, no aparecerá el menú contextual de copia..textSelection(.enabled): Esta es la joya de la corona. Al añadir este modificador al segundoText, le estamos diciendo al motor de renderizado de SwiftUI que habilite los gestos nativos de selección del sistema.
Si pruebas esto en el simulador de iOS (pulsación larga) verás los manejadores de selección azules y el menú emergente (Copiar, Compartir, etc.). Si cambias tu target en Xcode a tu Mac y lo ejecutas, verás que puedes usar el ratón para hacer clic y arrastrar sobre el texto para seleccionarlo. ¡Esto es la programación Swift en su máxima expresión!
El Poder de la Herencia en SwiftUI
Como iOS Developer, una de las cosas que más amarás de SwiftUI es cómo maneja el entorno (Environment). El modificador .textSelection no solo se aplica a una única vista Text, sino que puede propagarse a través de la jerarquía de vistas.
Imagina que tienes una pantalla de “Detalles de la Factura” con decenas de campos de texto. Sería muy tedioso y propenso a errores añadir .textSelection(.enabled) a cada uno de ellos. En su lugar, puedes aplicarlo al contenedor padre.
import SwiftUI
struct InvoiceDetailView: View {
var body: some View {
VStack(alignment: .leading, spacing: 15) {
Text("Detalles de la Factura")
.font(.title)
.bold()
VStack(alignment: .leading, spacing: 5) {
Text("Cliente: María García")
Text("Nº de Orden: #84759392")
Text("Fecha: 24 de Octubre, 2023")
Text("Total: 154.50 €")
}
.padding()
.background(Color.blue.opacity(0.05))
.cornerRadius(10)
Text("Gracias por su compra.")
.font(.footnote)
.foregroundColor(.gray)
}
.padding()
// Habilitamos la selección para toda la VStack principal
.textSelection(.enabled)
}
}
¿Qué sucede aquí?
Al aplicar Text Selection en SwiftUI al VStack más externo, todos los elementos Text contenidos dentro de él heredan esta propiedad. Ahora el usuario puede seleccionar el número de orden, el nombre del cliente o el total de la factura de forma individual.
Control Granular: Habilitar y Deshabilitar Text Selection
La herencia es fantástica, pero a veces necesitas excepciones. ¿Qué pasa si quieres que casi toda tu vista sea seleccionable, pero hay un texto específico (como un botón camuflado o un aviso legal) que no debe interactuar con este gesto para no confundir al usuario?
La programación Swift nos permite anular este comportamiento en vistas hijas específicas utilizando .textSelection(.disabled).
import SwiftUI
struct UserProfileView: View {
var body: some View {
Form {
Section(header: Text("Información Personal")) {
Text("ID de Usuario: UUID-9948-ABX")
Text("Email: developer@ejemplo.com")
}
Section(header: Text("Ajustes del Sistema")) {
Text("Versión de la App: 2.1.0")
Text("Información Confidencial. No copiar.")
.foregroundColor(.red)
// Sobrescribimos la herencia del contenedor padre
.textSelection(.disabled)
}
}
// Todo el formulario es seleccionable por defecto
.textSelection(.enabled)
}
}
En este ejemplo estructurado en Xcode, cualquier información dentro de la vista Form puede ser copiada por el usuario, a excepción de la advertencia roja que ha sido explícitamente deshabilitada. Esta granularidad es fundamental para construir interfaces robustas y profesionales.
Consideraciones Multiplataforma: iOS vs macOS vs watchOS
El verdadero arte de ser un iOS Developer versátil es comprender que, aunque SwiftUI unifica el código, no unifica (ni debe hacerlo) la experiencia de usuario, ya que cada dispositivo tiene un paradigma de interacción distinto.
En iOS e iPadOS
La implementación de Text Selection en SwiftUI se traduce en interacciones táctiles. Una pulsación larga invoca la lupa y los tiradores de selección (lollipops). Es importante tener cuidado si colocas un texto seleccionable dentro de elementos que ya tienen gestos propios, como un Button o un NavigationLink. SwiftUI es inteligente y suele priorizar el gesto del botón, lo que puede hacer que la selección de texto sea difícil o imposible. Si necesitas un texto seleccionable, colócalo fuera de las áreas de toque activas de navegación.
En macOS
El Mac es el rey del ratón y el teclado. Aquí, .textSelection(.enabled) permite el clásico clic y arrastre (drag) para resaltar texto, así como el doble clic para seleccionar una palabra y el triple clic para seleccionar un párrafo. El cursor cambia automáticamente al ícono de selección de texto (“I-beam”) cuando pasas el ratón por encima, proporcionando un excelente feedback visual, algo nativo de la programación Swift en macOS.
En watchOS
El Apple Watch presenta un desafío único. Sus pantallas son diminutas y el paradigma principal de interacción es visualización rápida y toques breves, no selección de texto ni manipulación de portapapeles.
En versiones actuales de watchOS, aplicar .textSelection(.enabled) generalmente no tiene un efecto interactivo profundo o simplemente es ignorado por el sistema, ya que watchOS carece de un portapapeles global accesible para el usuario de la misma manera que iOS.
Sin embargo, gracias a la naturaleza multiplataforma de SwiftUI en Xcode, incluir este modificador en tu código compartido no romperá tu aplicación en el Apple Watch. Simplemente, el sistema omitirá graciosamente la funcionalidad, permitiéndote mantener un único código base limpio (Code Reuse).
Listas y Colecciones: Un Reto Común en Xcode
Un problema habitual que te encontrarás como iOS Developer es aplicar la selección de texto dentro de un List. Debido a cómo UIKit y AppKit manejan internamente las celdas de las listas, aplicar la selección globalmente a veces puede tener comportamientos erráticos con los gestos de deslizamiento (swipe to delete).
La mejor práctica en Swift cuando trabajas con colecciones es aplicar el modificador directamente al contenido de la fila, en lugar de a toda la lista.
import SwiftUI
struct LogEvent: Identifiable {
let id = UUID()
let timestamp: String
let message: String
}
struct DebugConsoleView: View {
let logs = [
LogEvent(timestamp: "10:01:00", message: "App iniciada correctamente"),
LogEvent(timestamp: "10:01:05", message: "Error 404: No se pudo cargar el recurso de red"),
LogEvent(timestamp: "10:02:12", message: "Reintento de conexión exitoso")
]
var body: some View {
NavigationView {
List(logs) { log in
VStack(alignment: .leading) {
Text(log.timestamp)
.font(.caption)
.foregroundColor(.gray)
Text(log.message)
.font(.body)
}
// Aplicamos la selección directamente a la celda
.textSelection(.enabled)
}
.navigationTitle("Consola de Depuración")
}
}
}
Al hacerlo de esta manera, garantizas que el usuario pueda copiar ese molesto mensaje de “Error 404” para enviártelo por correo de soporte, sin interferir con la navegación nativa de la lista.
Creando un Modificador Personalizado (Custom View Modifier)
Si te encuentras escribiendo .textSelection(.enabled) constantemente en tu proyecto en Xcode, puedes abrazar la filosofía de la programación Swift y crear tu propio modificador semántico. Esto no solo limpia tu código, sino que también lo hace más legible.
import SwiftUI
// 1. Creamos el modificador
struct CopiableTextModifier: ViewModifier {
func body(content: Content) -> some View {
content
.textSelection(.enabled)
// Podemos añadir estilos extra para indicar que es copiable
.contextMenu {
Button(action: {
// Acción extra opcional
print("Texto interactuado")
}) {
Label("Texto Interactivo", systemImage: "info.circle")
}
}
}
}
// 2. Extendemos la vista para un uso más limpio
extension View {
func makeCopiable() -> some View {
self.modifier(CopiableTextModifier())
}
}
// 3. Uso en nuestra interfaz
struct CustomModifierExample: View {
var body: some View {
Text("Este es el hash de tu transacción: 0x8F4B2...")
.makeCopiable() // ¡Mucho más semántico!
.padding()
}
}
Esta es la marca de un verdadero iOS Developer sénior: adaptar las herramientas del framework para que se ajusten a la narrativa y arquitectura de tu propio código.
Accesibilidad y Experiencia de Usuario (UX)
Nunca podemos hablar de interfaces en Xcode sin mencionar la accesibilidad (VoiceOver). Afortunadamente, SwiftUI hace un trabajo fenomenal al integrar Text Selection en SwiftUI con los servicios de accesibilidad de Apple.
Cuando un texto es seleccionable, VoiceOver permite al usuario interactuar con él de forma más profunda a través del rotor, permitiéndoles copiar fragmentos al portapapeles sin necesidad de ver la pantalla. Como creador, no tienes que añadir código extra para que esto funcione; el simple hecho de usar .textSelection(.enabled) mejora inmediatamente la accesibilidad de tu app.
Cuándo NO usar Text Selection
Por candor y realismo, es importante saber cuándo detenerse. No todo el texto de tu aplicación debe ser seleccionable.
- Etiquetas de navegación: Los títulos de tu
NavigationBaro las pestañas de tuTabViewno necesitan ser copiables. - Botones: Como se mencionó, interfiere con la acción principal.
- Textos decorativos: Si el texto es puramente estético y no contiene información útil (como una marca de agua), deshabilitar la selección mantiene la interfaz limpia y evita frustraciones gestuales accidentales.
Conclusión
La introducción de Text Selection en SwiftUI es un testimonio claro de cómo la programación Swift continúa madurando, centrándose en proporcionar potentes herramientas a los desarrolladores con una sintaxis increíblemente concisa. Lo que antes requería decenas de líneas de código en UIKit y extensiones complejas en Xcode, ahora se resuelve con una elegante y única línea de código.








