Sugerir funcionalidades a los usuarios con TipKit
Misael Cuevas
04 octubre, 20239min de lectura
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 SwiftUIstruct ContentView: View {@State var number = 0var 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:
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 TipKitstruct CountTip: Tip {// 1var title: Text {Text("Press the text to count")}// 2var message: Text? {Text("The text will change when you tap it.")}// 3var image: Image? {Image(systemName: "hand.tap.fill")}}
title
: el título que se mostrará en el tip.message
: el mensaje que se mostrará en el tip.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.
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)
Para modificar el radio de las esquinas del TipView
puedes utilizar el modificador .tipCornerRadius(_:antialiased:)
. Por ejemplo:
TipView(countTip).tipCornerRadius(100)
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.
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)
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)
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
oText
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] {// 1Action(id: "action-ok", title: "Ok")// 2Action {invalidate(reason: .tipClosed)} _: {Text("Close").foregroundStyle(.red)}}
- Un
Action
con el identificador "action-ok" y el texto "Ok". - Un
Action
que, al ser presionado, cerrará el tip y mostrará el texto "Close" en color rojo a través deText
y el modificadorforegroundStyle
.
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 inif action.id == "action-ok" {countTip.invalidate(reason: .tipClosed)}}
Si estás utilizando .popoverTip
reemplaza el modificador con el siguiente código:
.popoverTip(countTip) { action inif 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 unaURL
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! 😉