Sugerir funcionalidades a los usuarios con TipKit

La WWDC de 2023 vino con muchas sorpresas, una de ellas fue la introducción de TipKit, un framework que permite mostrar sugerencias o tips a los usuarios de una app. Algunos casos de uso pueden ser dar visibilidad a funcionalidades ya existentes, pero que son poco conocidas, o mostrar nuevas funcionalidades introducidas en una actualización de una app.

TipKit está disponible a partir de dispositivos con iOS 17, iPadOS 17, macOS 14, watchOS 10 y tvOS 17.

App de ejemplo en SwiftUI

A modo de demostración, crea un proyecto de iOS con SwiftUI en Xcode 15 y reemplaza el contenido del archivo ContentView.swift con el siguiente código:

import SwiftUI
struct ContentView: View {
@State var number = 0
var body: some View {
VStack {
Text("\(number)")
.onTapGesture { number += 1 }
.font(.title)
}
.padding()
}
}
#Preview {
ContentView()
}

Este código define una vista de SwiftUI que cuenta el número de veces que se presiona el texto. Utiliza una variable de estado number para almacenar la cantidad de veces que se presiona el texto y muestra el número en un formato de título.

Al ejecutar el proyecto, la app de ejemplo debe verse como se muestra a continuación:

Ejemplo de la vista de SwiftUI

Como puedes ver, esta vista no comunica claramente la funcionalidad de presionar el texto al usuario. En este artículo, aprenderás cómo utilizar TipKit para sugerir esta funcionalidad de manera efectiva.

Habilitar TipKit

Antes de crear tu primer tip, debes habilitar TipKit al inicio del lanzamiento de tu app. Para hacerlo, abre la estructura que conforma el protocolo App, añade import TipKit y el siguiente código después de la definición de la vista principal que se encuentra dentro del WindowGroup:

.task {
try? Tips.configure()
}

Tips.configure(_:) carga y configura los estados de todos los tips de la app.

Otra forma de habilitar TipKit es creando un inicializador en la estructura que conforma el protocolo App:

init() {
try? Tips.configure()
}

Con fines de este artículo, añade try? Tips.resetDatastore() antes de try? Tips.configure(_:) para reiniciar todos los tips a su estado inicial y permitir mostrar los tips cada vez que ejecutes la app en el simulador o dispositivo. Tu código debe quedar así:

.task {
try? Tips.resetDatastore()
try? Tips.configure()
}

Si quieres probar los tips desde el Canvas de Xcode agrega el código anterior dentro de la #Preview de la ContentView, ejemplo:

#Preview {
ContentView()
.task {
try? Tips.resetDatastore()
try? Tips.configure()
}
}

Recuerda eliminar try? Tips.resetDatastore() antes de publicar tu app en producción.

Crear un tip

Para crear tu primer tip, necesitas crear una estructura que implemente el protocolo Tip. Este protocolo define el contenido de tu tip. A continuación, crea una estructura con el nombre CountTip y añade el siguiente código:

import TipKit
struct CountTip: Tip {
// 1
var title: Text {
Text("Press the text to count")
}
// 2
var message: Text? {
Text("The text will change when you tap it.")
}
// 3
var image: Image? {
Image(systemName: "hand.tap.fill")
}
}
  1. title: el título que se mostrará en el tip.
  2. message: el mensaje que se mostrará en el tip.
  3. image: la imagen que se mostrará en el tip.

Estas propiedades reciben vistas de SwiftUI, por lo que puedes utilizar modificadores para personalizarlas según tus preferencias, como el modificador .foregroundStyle para cambiar el color.

Agregar un tip a una vista

Lo primero que debes hacer es importar TipKit en el ContentView con import TipKit y después crear una instancia del tip CountTip:

var countTip = CountTip()

Con la instancia creada, existen dos maneras de mostrar un tip en una vista de SwiftUI: Inline y Popover.

Inline tip

Se muestra dentro de la vista actual con un diseño similar a una tarjeta o burbuja, respetando los elementos de la interfaz de usuario ya añadidos. Para añadir un Inline Tip encima del Text, utiliza:

TipView(countTip)

Al ejecutar la app, verás el Inline Tip con el título, mensaje e imagen que has definido en CountTip, además de un botón con una X para cerrar el tip.

Vista del inline tip

TipView tiene un parámetro arrowEdge que te permite mostrar una flecha en el lado que elijas. Por ejemplo, si deseas una flecha hacia abajo, utiliza:

TipView(countTip, arrowEdge: .bottom)
Vista del Inline tip con una flecha hacia abajo

Para modificar el radio de las esquinas del TipView puedes utilizar el modificador .tipCornerRadius(_:antialiased:). Por ejemplo:

TipView(countTip)
.tipCornerRadius(100)
Vista del Inline tip con un radio en las esquinas personalizado

Popover tip

Utiliza el modificador .popoverTip(_:arrowEdge:action:) para mostrar el tip encima de la vista principal y anclado al elemento que tiene este modificador.

Para que no muestres el mismo tip dos veces procede a remover el inline tip TipView y reemplaza el Text por el siguiente código:

Text("\(number)")
.onTapGesture { number += 1 }
.font(.title)
.popoverTip(countTip)

Al ejecutar la app, verás el tip anclado al texto y una ligera sombra para indicar al usuario que el tip está encima de la vista principal.

Vista del Popover tip

Por defecto, se incluye una flecha que señala la vista a la que está anclado el tip. Si deseas modificarlo, de la misma manera que con TipView, utiliza el parámetro arrowEdge. Esto también cambiará la posición del tip. Por ejemplo, si deseas que la flecha esté debajo:

.popoverTip(countTip, arrowEdge: .bottom)
Vista del Popover tip con una flecha hacia abajo

A partir de esta sección, lo que aprenderás aplica para ambas formas de mostrar un tip por lo que utiliza el que más te guste, ya sea TipView o .popoverTip(_:arrowEdge:action:).

Cerrar tip manualmente

Además de cerrar el tip presionando el botón X, puedes hacerlo manualmente utilizando la función invalidate(reason:), a la que debes proporcionar una razón para describir porque el tip fue invalidado. Las razones pueden ser:

  • actionPerformed: indica que la acción se ha ejecutado según lo descrito en el tip.
  • displayCountExceeded: indica que el tip se ha mostrado más veces de las permitidas.
  • tipClosed: indica que el usuario ha cerrado el tip mientras se mostraba.

Por ejemplo, si deseas cerrar el tip cuando el usuario ha pulsado el Text, añade lo siguiente en dentro de su modificador .onTapGesture:

countTip.invalidate(reason: .actionPerformed)
Invalidar tip manualmente

Agregar botones al tip

Es muy sencillo agregar botones a tus tips, ya que el protocolo Tip incluye la variable var actions: [Action], que requiere una lista de Tips.Actions.

Tips.Actions tiene dos inicializadores para crear un botón: init(id:perform:_:) e init(id:title:perform:). Estos permiten:

  • Añadir un identificador opcional en el parámetro id.
  • Utilizar un String o Text como título del botón.
  • Proporcionar un closure en el parámetro perform para especificar la acción que se realizará cuando el usuario presione el botón.

Abre la estructura CountTip.swift e inserta el siguiente código después de la variable image:

var actions: [Action] {
// 1
Action(id: "action-ok", title: "Ok")
// 2
Action {
invalidate(reason: .tipClosed)
} _: {
Text("Close")
.foregroundStyle(.red)
}
}
  1. Un Action con el identificador "action-ok" y el texto "Ok".
  2. Un Action que, al ser presionado, cerrará el tip y mostrará el texto "Close" en color rojo a través de Text y el modificador foregroundStyle.
tip con acciones

Otra forma de manejar la acción del botón desde la vista de SwiftUI es utilizando el parámetro action en TipView o el modificador .popoverTip. Esto es simplemente un closure que contiene el Action seleccionado.

Si estás utilizando TipView reemplaza la vista con el siguiente código:

TipView(countTip) { action in
if action.id == "action-ok" {
countTip.invalidate(reason: .tipClosed)
}
}

Si estás utilizando .popoverTip reemplaza el modificador con el siguiente código:

.popoverTip(countTip) { action in
if action.id == "action-ok" {
countTip.invalidate(reason: .tipClosed)
}
}

En ambos casos, se valida el id de la acción que el usuario ha presionado y se procede a cerrar el tip.

Otras configuraciones

Con Tips.configure(_:), puedes agregar configuraciones globales a todos los tips de tu app. Estas configuraciones incluyen .displayFrequency(_:) y .datastoreLocation(_:).

  • .displayFrequency(_:) define la frecuencia con la que se mostrarán los tips. Pueden mostrarse todos los tips de inmediato o uno solo cada hora, día, semana o mes.
  • .datastoreLocation(_:) define dónde se guardarán los datos de los tips. Pueden guardarse en el directorio por defecto de la app, en un contenedor de grupo o en una URL específica.
try? Tips.configure([
.displayFrequency(.immediate),
.datastoreLocation(.applicationDefault)
])

En este ejemplo, se configuran los tips para mostrarse de inmediato y almacenarse en el directorio por defecto de la app.

Otras configuraciones que puedes aplicar a un tip específico son establecer el número máximo de veces que se mostrará antes de que se invalide automáticamente el tip con MaxDisplayCount, o ignorar la frecuencia de visualización con IgnoresDisplayFrequency. Estas dos opciones se utilizan dentro de la propiedad options: [TipOption] del protocolo Tip. Por ejemplo:

Para mostrar el tip solo 2 veces:

var options: [TipOption] {
MaxDisplayCount(2)
}

Para ignorar la frecuencia de visualización del tip:

var options: [TipOption] {
IgnoresDisplayFrequency(true)
}

Estas dos opciones anulan la configuración global establecida con .displayFrequency(_:) para el tip en el que se especifican.

Por último, además de Tips.resetDatastore(), para realizar pruebas, puedes utilizar:

  • try? Tips.showAllTipsForTesting(_:): para mostrar todos o algunos tips.
  • try? Tips.hideTipsForTesting(_:): para ocultar todos o algunos tips.

Recuerda que estos dos últimos deben colocarse antes de Tips.configure(_:).

Conclusiones

Apple recomienda que todos los tips sean accionables, instructivos y fáciles de recordar. Además, TipKit permite sincronizar el estado de un tip a través de iCloud para garantizar que un tip visto en un dispositivo no se muestre en otros.

¡No olvides compartir con nosotros tus tips en nuestras redes sociales! 😉

Comparte este artículo

Subscríbete a nuestro Newsletter

Mantente al día en el mundo de las aplicaciones móviles con nuestro blog especializado.

Artículos semanales

Todas las semanas artículos nuevos sobre el mundo de las aplicaciones móviles.

No spam

No te enviaremos spam, solo contenido de calidad. Puedes darte de baja cuando quieras.

Contenido de calidad

Nada de contenido generado de manera automática usando ChatGPT.

Recomendaciones

Tips indispensables sobre mejores prácticas y metodologías.

© 2024 AsyncLearn