Se pueden presentar consejos en la pantalla para dar información breve al usuario acerca de cómo usar una funcionalidad. La base de esto es el protocolo Tip de "TipKit". Se debe crear una estructura de datos que conforme este protocolo y definir las propiedades title (único obligatorio, el resto opcionales), message, image, id, actions, rules, options
import TipKit
struct SaveTip: Tip {
static let itemAdded = Event(id: "itemAdded")
var id: String = "tip-id-save"
var title: Text {
Text("Press to save")
}
var message: Text? {
Text("Press this button to save your progress.")
}
var actions: [Action] {[
.init(id: "action-id-save", title: "Got it!"),
.init(id: "action-id-perform", title: "Yo trabajo solo") {
print("Se presionó action perform")
self.invalidate(reason: .actionPerformed)
}
]}
var rules: [Rule] {[
#Rule(Self.itemAdded) {
$0.donations.count > 3
}
]}
}
Para usar Tips se debe invocar Tips.configure(_:) en el arranque de la aplicación, ANTES de que se muestren los tips en pantalla. Esto carga y configura el estado persistente de los Tips.
Se puede presentar un Tip como vista independiente por medio del componente TipView:
También se puede pintar un Tip señalando otro componente de la pantalla
struct GeneralInfoTip: Tip {
var id: String = "tip-id-general"
var title: Text {
Text("Tutorial de tips")
}
var message: Text? {
Text("Presiona este botón para agregar un item al carrito.")
}
var options: [any TipOption] {[
MaxDisplayCount(2)
]}
}
struct ContentView: View {
private let saveTip = SaveTip()
private let generalTip = GeneralInfoTip()
init() {
try? Tips.configure([
.displayFrequency(.immediate),
.datastoreLocation(.applicationDefault)
])
// ⚠️ Descomentar para mostrar los tips
// Tips.showAllTipsForTesting()
// ⚠️ Descomentar para ocultar los tips
// Tips.hideAllTipsForTesting()
}
@State private var dialogShowing: Bool = false
var body: some View {
VStack {
// TipView(generalTip)
Button("Agregar al carrito") {
Task {
print(SaveTip.itemAdded.donations.count)
await SaveTip.itemAdded.donate()
}
}
.popoverTip(generalTip)
Button("Guardar items") {
print("Estoy guardando algo")
}
.popoverTip(saveTip, arrowEdge: .top) { action in
if action.id == "action-id-save" {
print("Pulsé Got it")
saveTip.invalidate(reason: .actionPerformed)
Task {
try! await SaveTip.itemAdded.deleteDonations()
}
}
}
}
.padding()
}
}
Como las instancias de Tip se cierran y posiblemente no se vuelven a presentar en un largo tiempo, para hacer pruebas conviene utilizar los siguientes métodos:
-
Tips.showAllTipsForTesting(): Muestra todos los tips. -
Tips.hideAllTipsForTesting(): Desactiva todos los tips. -
Tips.resetDatastore(): Reinicia la base de datos de tips. Útil para probar el primer arranque de la aplicación. Se debe llamar antes deTips.configure(_:).
Manejo de acciones
Se puede o no agregar acciones a un Tip. En caso de que se haga, se las puede manejar directamente en la estructura que define el Tip, o en la vista donde se presenta.
Se puede hacer directamente en la estructura Tip, como se ilustró en SaveTip con "action-id-perform". Notar que al final se llama invalidate(reason:) - Si no se invoca este método, el Tip nunca se cierra.
.init(id: "action-id-perform", title: "Yo trabajo solo") {
print("Se presionó action perform")
self.invalidate(reason: .actionPerformed)
}
También se puede manejar la acción desde la vista que presenta el Tip como se ilustró en ContentView con "action-id-save".
.popoverTip(saveTip, arrowEdge: .top) { action in
if action.id == "action-id-save" {
print("Pulsé Got it")
saveTip.invalidate(reason: .actionPerformed)
Task {
try! await SaveTip.itemAdded.deleteDonations()
}
}
}
Condiciones de presentación de un Tip
La presentación puede ser condicionada con Rule. Si se quiere dar un comportamiento dinámico a la regla, se debe agregar un Event.
La definición de la regla se ilustra en SaveTip.
// ⚠️ Definición de Event
static let itemAdded = Event(id: "itemAdded")
// ⚠️ Definición de la regla dinámica
var rules: [Rule] {[
#Rule(Self.itemAdded) {
$0.donations.count > 3
}
]}
Luego, en ContentView se pueden agregar "donaciones" con donate() y limpiarlas con deleteDonations():
// ⚠️ Se agregan donaciones
Task {
print(SaveTip.itemAdded.donations.count)
await SaveTip.itemAdded.donate()
}
// ⚠️ Se limpian las donaciones
Task {
try! await SaveTip.itemAdded.deleteDonations()
}


Top comments (0)