DEV Community

GoyesDev
GoyesDev

Posted on

[SUI] TipKit

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
    }
  ]}
}
Enter fullscreen mode Exit fullscreen mode

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)
  ]}
}
Enter fullscreen mode Exit fullscreen mode
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()
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

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)
}
Enter fullscreen mode Exit fullscreen mode

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()
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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
  }
]}
Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

Bibliografía

Top comments (0)