Drag and Drop en français
Introduction au Drag and Drop en SwiftUI
Le concept de Drag and Drop (glisser-déposer) dans SwiftUI permet de déplacer et de déposer des objets, comme des éléments d'interface utilisateur, sur l'écran. Cette fonctionnalité enrichit l'expérience utilisateur en facilitant la réorganisation et le transfert d'objets à travers différentes parties de l'interface de l'application.
Déplacement d'éléments transférables
Le protocole Transferable de SwiftUI simplifie l'ajout de l'expérience de glisser-déposer dans une application. Cela peut être facilement réalisé en utilisant les modificateurs .draggable()
et .dropDestination()
.
Glisser des éléments
Pour activer la fonctionnalité de glisser, il suffit d'attacher le modificateur de vue .draggable()
et de spécifier l'objet que l'on souhaite déplacer. Cet objet doit se conformer au protocole Transferable. Voici un exemple de code qui affiche quatre chaînes représentant des produits Apple et permet à l'utilisateur de les faire glisser vers une boîte en dessous :
struct ContentView: View {
@State private var appleProducts: [String] = ["iphone", "macbook.and.iphone", "ipad", "applewatch.watchface"]
@State private var dropImage = Image(systemName: "square.and.arrow.down")
var body: some View {
VStack {
HStack {
ForEach(appleProducts, id: \.self) { product in
Image(systemName: product)
.frame(minWidth: 50, minHeight: 50)
.background(.white)
.foregroundStyle(.black)
.cornerRadius(10)
.draggable(product)
}
}
.frame(minWidth: 250, minHeight: 70)
.cornerRadius(10)
.background(.yellow)
}
}
}
Déposer des éléments
Pour gérer l'action de dépôt, utilisez le modificateur .dropDestination()
, qui a deux paramètres : - items
: un tableau contenant tous les éléments glissés. Si un seul élément est glissé, ce tableau contiendra uniquement un élément. - location
: position de l'élément glissé dans la vue de dépôt.
struct ContentView: View {
@State private var appleProducts: [String] = ["iphone", "macbook.and.iphone", "ipad", "applewatch.watchface"]
@State private var dropImage = Image(systemName: "square.and.arrow.down")
var body: some View {
VStack {
HStack {
ForEach(appleProducts, id: \.self) { product in
Image(systemName: product)
.frame(minWidth: 50, minHeight: 50)
.background(.white)
.foregroundStyle(.black)
.cornerRadius(10)
.draggable(product)
}
}
.frame(minWidth: 250, minHeight: 70)
.cornerRadius(10)
.background(.yellow)
Text("Déposez les éléments ici :")
dropImage
.frame(width: 200, height: 150)
.background(.blue)
.foregroundStyle(.white)
.cornerRadius(10)
.dropDestination(for: Image.self) { items, location in
dropImage = items.first ?? Image(systemName: "square.and.arrow.down")
print(location)
return true
}
}
}
}
Utiliser des fournisseurs d’éléments
DropDelegate est un protocole dans SwiftUI qui permet aux développeurs de gérer les opérations de glissement et de dépôt de manière flexible. Pour permettre à une vue d'accepter des dépôts, utilisez la méthode onDrop()
et spécifiez un délégué de dépôt.
Ce code définit un délégué de dépôt qui gère différents événements liés au dépôt d'objets sur une vue :
struct CDDropDelegate: DropDelegate {
func dropEntered(info: DropInfo) {
// Déclenché lorsqu'un objet entre dans la vue.
}
func dropExited(info: DropInfo) {
// Déclenché lorsqu'un objet sort de la vue.
}
func dropUpdated(info: DropInfo) -> DropProposal? {
// Déclenché lorsqu'un objet se déplace dans la vue.
}
func validateDrop(info: DropInfo) -> Bool {
// Détermine si le dépôt doit être accepté ou rejeté.
}
func performDrop(info: DropInfo) -> Bool {
// Gère le dépôt lorsque l'utilisateur dépose un objet dans la vue.
}
}
Proposition de dépôt
DropProposal est une structure de SwiftUI qui aide à contrôler comment les objets et les opérations de dépôt doivent être gérés lors du dépôt dans une vue SwiftUI. Elle est utilisée avec le modificateur .onDrop()
pour déterminer ce qui se passe lorsque l'utilisateur effectue un dépôt. Cette proposition peut être personnalisée via la propriété operation
pour spécifier l'opération de dépôt (ex: .move
ou .copy
).
Voici un exemple :
struct ContentView: View {
@State private var text: String = ""
var body: some View {
VStack {
Text("Exemple de DropProposal")
.font(.largeTitle)
.padding()
Text("Déposez le texte ici :")
Text(text)
.padding()
.background(Color.yellow)
.onDrop(of: [UTType.text], isTargeted: $text?, perform: { providers, isTargeted in
// Créer une proposition de dépôt personnalisée
let dropProposal = DropProposal(operation: .copy)
if isTargeted {
if let itemProvider = providers.first {
itemProvider.loadObject(ofClass: String.self) { item, error in
if let droppedText = item as? String {
DispatchQueue.main.async {
self.text = droppedText
}
}
}
return dropProposal
}
}
return nil
})
.frame(width: 200, height: 100)
.border(Color.gray)
}
}
}
Informations sur le dépôt
DropInfo est une structure qui fournit des informations sur une action de dépôt. Il est souvent utilisé lors de la gestion des dépôts avec le modificateur onDrop()
. DropInfo fournit des détails sur l'emplacement du dépôt, les types de données déposées, et d'autres informations importantes.
Exemple utilisant .onDrag() et .onDrop()
Voici un exemple illustrant un LazyVGrid
simple avec quatre éléments. Le modificateur de vue .onDrag()
est appliqué à chaque élément pour gérer le glissement, tandis que le modificateur .onDrop()
permet de déplacer et de réorganiser les éléments, le résultat étant conservé dans une variable d'état nommée draggedItem
.
struct ContentView: View {
@State private var items = ["Apple", "Banana", "Lemon", "Orange"]
@State private var draggedItem: String?
var body: some View {
VStack {
LazyVGrid(columns: [GridItem(.adaptive(minimum: 160), spacing: 15)], spacing: 15) {
ForEach(items, id: \.self) { item in
Text(item)
.background(Color.green)
.font(.headline)
.foregroundColor(.white)
.shadow(color: .black, radius: 3)
.frame(maxWidth: 136)
.padding()
.onDrag({
self.draggedItem = item
return NSItemProvider(item: nil, typeIdentifier: item)
})
.onDrop(of: [UTType.text], delegate: MyDropDelegate(item: item, items: $items, draggedItem: $draggedItem))
}
}
}
}
}
struct MyDropDelegate: DropDelegate {
let item: String
@Binding var items: [String]
@Binding var draggedItem: String?
func performDrop(info: DropInfo) -> Bool {
return true
}
func dropEntered(info: DropInfo) {
guard let draggedItem = self.draggedItem else { return }
if draggedItem != item {
let from = items.firstIndex(of: draggedItem)!
let to = items.firstIndex(of: item)!
withAnimation(.default) {
self.items.move(fromOffsets: IndexSet(integer: from), toOffset: to > from ? to + 1 : to)
}
}
}
}