iOS 11 a élevé iOS, en particulier pour l'iPad, en une véritable plateforme multitâches, grâce à Drag and Drop. Cela promet de brouiller les frontières entre les applications, ce qui permet de partager facilement le contenu. Tirant parti de la technologie multi-touch, iOS 11 permet de déplacer le contenu de manière naturelle et intuitive, en rapprochant les appareils mobiles d’Apple de la richesse dont bénéficient ses utilisateurs de bureau et d’ordinateur portable..
Cette fonctionnalité attendue depuis longtemps vous permet de faire glisser des éléments vers un autre emplacement de la même application ou vers une autre application. Cela fonctionne soit avec un écran partagé, soit via le dock, en utilisant un geste continu. De plus, les utilisateurs ne sont pas limités à la sélection d'un seul élément mais peuvent faire glisser plusieurs éléments en même temps. De nombreuses applications, y compris les applications système telles que Photos et Fichiers, tirent parti de la sélection multiple et du déplacement de plusieurs fichiers..
Ce didacticiel vous présentera les techniques de glisser-déposer, puis approfondira l'architecture et la stratégie d'utilisation du nouveau SDK de glisser-déposer dans une application alimentée par une vue de tableau. Je souhaite aider les développeurs tels que vous à adapter vos applications au comportement de l'interface utilisateur qui deviendra la norme dans les futures applications iOS..
Dans ce tutoriel, nous allons couvrir les points suivants:
Dans la seconde moitié de ce didacticiel, nous allons passer en revue les étapes pratiques consistant à permettre à une application de vue tableau simple de tirer parti du glisser-déposer, en commençant par l'un des modèles de vue de table par défaut d'Apple disponibles lors de la création d'un nouveau projet dans Xcode. 9. Allez-y et clonez le dépôt GitHub du tutoriel si vous souhaitez suivre.
Ce didacticiel suppose que vous avez de l’expérience en tant que développeur iOS et que vous avez utilisé des bibliothèques UIKit dans Swift ou Objective-C, notamment: UITableView
, et que vous connaissez un peu les délégués et les protocoles.
À l'aide de la nomenclature Apple, un élément visuel est déplacé de l'emplacement source vers l'objet de destination. Cette activité s'appelle une activité de glisser-déposer, soit dans une seule application (prise en charge d'iPad et d'iPhone), soit dans deux applications (uniquement disponible sur iPad)..
Lorsqu'une session de glisser-déposer est en cours, les applications source et cible sont toujours actives et s'exécutent normalement, prenant en charge les interactions utilisateur. En fait, contrairement à macOS, iOS prend en charge plusieurs activités de glisser simultanées à l'aide de plusieurs doigts..
Mais concentrons-nous sur un élément à glisser et sur la manière dont il utilise une promesse comme contrat pour ses représentations de données..
Chaque élément glissé peut être considéré comme une promesse, une représentation de données contenue qui sera glissée et déposée de la source à la destination. L’élément de glisser utilise un fournisseur d’élément, remplissant son registeredTypeIdentifiers
avec des identificateurs de type uniformes, qui sont des représentations de données individuelles qu'il s'engage à fournir à la destination souhaitée avec une image d'aperçu (qui est épinglée visuellement sous le point de contact de l'utilisateur), comme illustré ci-dessous:
L'élément glisser est construit à travers le UIDragInteractionDelegate
depuis l’emplacement source et gérées sur l’emplacement de destination via le UIDropInteractionDelegate
. L’emplacement source doit être conforme à la NSItemProviderWriting
protocole, et l’emplacement de destination doit être conforme à la NSItemProviderLecture
protocole.
Voilà un aperçu de base de la nomination d'une vue en tant qu'élément de glisser, par le biais de promesses. Voyons comment nous implémentons une source de glissement à partir d'une vue, avant d'établir la destination de dépôt..
En concentrant notre attention sur la première partie du glisser-déposer, la source du glisser, nous devons suivre les étapes suivantes lorsque l'utilisateur lance une activité de glisser:
UIDragInterationDelegate
protocole.dragInteraction (_: itemsForBeginning :)
. La première chose à faire est de conformer la vue proposée à la UIDragInterationDelegate
protocole, en créant un nouveau UIDragInteraction
par exemple et en l'associant à votre ViewController
La vue de addInteraction
propriété ainsi que son délégué, comme suit:
let dragInteraction = UIDragInteraction (delegate: dragInteractionDelegate) view.addInteraction (dragInteraction)
Après avoir déclaré votre source de glissement, vous créez un élément de glissement, essentiellement une promesse de représentation des données, en implémentant la méthode déléguée. dragInteraction (_: itemsForBeginning :)
, que le système appelle pour renvoyer un tableau d'un ou plusieurs éléments glissés afin de renseigner la propriété des éléments de la session glissée. L’exemple suivant crée un NSItemProvider
à partir d'une image, avant de retourner un tableau d'éléments de données:
func dragInteraction (_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] guard laisse à imagePromise = imageView.image else return [] // en renvoyant un tableau vide, vous désactivez le glissement. let provider = NSItemProvider (objet: imagePromise) let item = UIDragItem (itemProvider: fournisseur) renvoie [élément]
La méthode déléguée ci-dessus répond à une demande de glisser déclenchée lorsque l'utilisateur commence à faire glisser l'élément avec la reconnaissance de geste (UIGestureRecognizer
) renvoyer un message de type «glisser commencé» au système. C’est ce qui initialise essentiellement la "session de drag".
Ensuite, nous continuons avec l’implémentation de la destination de dépôt, pour gérer le tableau des éléments glissés initiés dans la session..
De même, pour que votre vue désignée accepte et utilise les données en tant que partie de la destination de dépôt, vous devez suivre les étapes suivantes:
DropInteraction
.dropInteraction (_: canHandle :)
.dropInteraction (_: sessionDidUpdate :)
méthode de protocole, indiquant si vous allez copier, déplacer, refuser ou annuler la session.dropInteraction (_: performDrop :)
méthode du protocole.Tout comme nous avons configuré notre vue pour activer le glissement, nous allons configurer symétriquement notre vue nommée pour accepter les éléments supprimés d’une session de glisser, en utilisant la commande UIDropinteractionDelegate
et mettre en œuvre ses DropInteraction
méthode déléguée:
let dropInteraction = UIDropInteraction (delegate: dropInteractionDelegate) view.addInteraction (dropInteraction)
Pour indiquer si une vue est capable d’accepter des éléments glissés ou si elle refuse, nous implémentons la dropInteraction (_: canHandle :)
méthode du protocole. La méthode suivante permet à notre vue d'indiquer au système s'il peut accepter les éléments, en indiquant le type d'objets qu'il peut recevoir. Dans ce cas, UIImages.
func dropInteraction (_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool // Indiquez explicitement le type d'élément de dépôt acceptable ici. return session.canLoadObjects (ofClass: UIImage.self)
Si l'objet de la vue n'accepte aucune interaction de suppression, vous devez renvoyer false à partir de cette méthode..
Ensuite, liez une proposition de suppression pour accepter les données de la session de suppression. Bien qu’il s’agisse d’une méthode facultative, il est vivement recommandé de l’implémenter, car elle indique de manière visuelle si la suppression entraînera la copie de l’élément, son déplacement, ou si la suppression sera totalement refusée. En mettant en œuvre le dropInteraction (_: sessionDidUpdate :)
méthode de protocole, qui retourne un UIDropProposal
, vous indiquez le type de proposition à l'aide du type d'énumération d'opération spécifique (UIDropOperation
). Les types valides que vous pouvez retourner sont:
Annuler
interdit
copie
bouge toi
func dropInteraction (_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal // Indique au système que vous allez déplacer l'élément de l'application source (vous pouvez également indiquer .copy pour copier plutôt que pour le déplacer) renvoyer UIDropProposal ( opération: .move)
Et enfin, pour utiliser le contenu de l’élément de données dans votre emplacement de destination, vous implémentez la dropInteraction (_: performDrop :)
méthode de protocole dans la file d'attente en arrière-plan (plutôt que dans la file principale, cela garantit la réactivité). Ceci est illustré ci-dessous:
func dropInteraction (_ interaction: UIDropInteraction, session performDrop: UIDropSession) // Consomme les éléments de glissement UIImage session.loadObjects (ofClass: UIImage.self) éléments dans let images = items as! [UIImage] self.imageView.image = images.first
Nous avons montré comment implémenter le glisser-déposer dans une vue personnalisée. Passons maintenant à la partie pratique de ce didacticiel et implémentons le glisser-déposer dans une application avec une vue sous forme de tableau..
Jusqu'à présent, nous avons discuté de la manière d'implémenter le glisser-déposer dans les vues personnalisées, mais Apple a également simplifié l'augmentation des vues de tableau et de collection par glisser-déposer. Alors que les champs de texte et les vues prennent automatiquement en charge le glisser-déposer en dehors de la boîte, les vues de table et de collection exposent des méthodes, des délégués et des propriétés spécifiques pour la personnalisation de leurs comportements de glisser-déposer. Nous allons jeter un coup d'oeil à cela sous peu.
Commencez par créer un nouveau projet dans Xcode 9 en vous assurant de sélectionner App Master-Detail depuis la fenêtre du modèle:
Avant de commencer à travailler sur le reste des étapes, poursuivez, construisez et exécutez le projet et jouez-y un peu. Vous verrez qu'il génère une nouvelle date d'horodatage lorsque vous sélectionnez le plus (+) bouton. Nous allons améliorer cette application en permettant à l'utilisateur de faire glisser et de commander les horodatages.
Le glisser-déposer est pris en charge dans les vues sous forme de tableau (ainsi que dans les collections) via des API spécialisées permettant le glisser-déposer avec des lignes, en conformant notre vue sous forme de tableau pour adopter à la fois la UITableViewDragDelegate
et UITableViewDropDelegate
protocoles. Ouvrez le MasterViewController.swift déposer et ajouter ce qui suit au viewDidLoad ()
méthode:
remplacer func viewDidLoad () super.viewDidLoad ()… self.tableView.dragDelegate = self self.tableView.dropDelegate = self…
Comme nous l'avons fait avec les vues personnalisées, nous devons gérer la nouvelle session de glisser lorsque l'utilisateur fait glisser une ligne sélectionnée ou plusieurs lignes / sélections. Nous faisons cela avec la méthode delegate tableView (_: itemsForBeginning: at :)
. Dans cette méthode, vous renvoyez un tableau rempli qui commence à faire glisser les lignes sélectionnées ou un tableau vide pour empêcher l'utilisateur de faire glisser le contenu de ce chemin d'index spécifique..
Ajoutez la méthode suivante à votre MasterViewController.swift fichier:
func tableView (_ tableView: UITableView, itemsForBeginning session: UIDragSession, à indexPath: IndexPath) -> [UIDragItem] let dateItem = self.objects [indexPath.row] comme! String let data = dateItem.data (using: .utf8) let itemProvider = NSItemProvider () itemProvider.registerDataRepresentation (forTypeIdentifier: kUTTypePlainText as String, visibilité: .all) complétion (data, nil) return nil return [UIDragItem ( itemProvider: itemProvider)]
Une partie du code ajouté devrait déjà vous être familière dans la section précédente, mais nous essayons essentiellement de créer un élément de données à partir de l'objet sélectionné, de l'envelopper dans un NSItemProvider
, et le retourner dans un DragItem
.
Passons maintenant à l'activation de la session de suppression, puis ajoutez les deux méthodes suivantes:
func tableView (_ tableView: UITableView, canHandle session: UIDropSession) -> Bool return session.canLoadObjects (ofClass: NSString.self) func tableView (_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIexpath UITableViewDropProposal si tableView.hasActiveDrag si session.items.count> 1 return UITableViewDropProposal (opération: .cancel) else retour UITableViewDropProposal (opération: .move, intent: .insertAtDestinationIndexPath) autre retour retournablePropal. copie, intention: .insertAtDestinationIndexPath)
La première méthode indique au système qu'il peut gérer les types de données String dans le cadre de sa session de suppression. La seconde méthode déléguée, tableView (_: dropSessionDidUpdate: withDestinationIndexPath :)
, suit le lieu de dépôt potentiel, en notifiant la méthode à chaque modification. Il affiche également un retour visuel pour permettre à l'utilisateur de savoir si un emplacement spécifique est interdit ou acceptable, à l'aide d'une petite icône visuelle cue.
Enfin, nous gérons le dépôt et consommons l’élément de données, en appelant tableView (_: performDropWith :)
, récupérer la ligne de l'élément de données glissé, mettre à jour la source de données de notre vue tableau et insérer les lignes nécessaires dans la table.
func tableView (_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) let destinationIndexPath: IndexPath si let indexPath = coordinator.destinationIndexPath destinationIndexPath = indexPath else // Récupère le dernier chemin d'index de la vue de table. let section = tableView.numberOfSections - 1 let row = tableView.numberOfRows (inSection: section) destinationIndexPath = IndexPath (ligne: ligne, section: section) coordinator.session.loadObjects (ofClass: NSString.self) éléments dans // Consume faites glisser des éléments. laisser stringItems = items en tant que! [Chaîne] var indexPaths = [IndexPath] () pour (index, élément) dans stringItems.enumerated () let indexPath = lignePath (ligne: destinationIndexPath.row + index, section: destinationIndexPath.section) self.objects.insert (élément , à: indexPath.row) indexPaths.append (indexPath) tableView.insertRows (à: indexPaths, avec: .automatic)
Pour plus d'informations sur la prise en charge du glisser-déposer dans vos vues de table, consultez la propre documentation destinée aux développeurs d'Apple sur la prise en charge du glisser-déposer dans les vues de table..
Le contenu que nous avons abordé devrait vous aider à mettre en œuvre le glisser-déposer dans vos applications, permettant ainsi aux utilisateurs de déplacer de manière visuelle et interactive le contenu dans leurs applications existantes, ainsi que parmi les applications..
Parallèlement aux connaissances techniques sur la mise en œuvre du glisser-déposer, il est impératif de prendre le temps de réfléchir à la manière de mettre en œuvre le glisser-déposer, en suivant les meilleures pratiques préconisées par Apple dans ses Human Interface Guidelines (HIG), en particulier. afin de fournir aux utilisateurs la meilleure expérience utilisateur possible pour iOS 11.
Pour terminer, nous aborderons certains des aspects les plus importants à prendre en compte, à commencer par les indices visuels. Selon le HIG, l'expérience fondamentale du glisser-déposer est que, lorsqu'un utilisateur interagit avec un contenu, des indices visuels indiquent à l'utilisateur une session de glisser active, indiquée par la montée de l'élément de contenu, ainsi qu'un badge indiquant ou n'est pas possible.
Nous avons déjà utilisé cette meilleure pratique dans nos exemples précédents, lorsque nous avons inclus le tableView (_: dropSessionDidUpdate: withDestinationIndexPath :)
méthode, indiquant si la destination de dépôt est un déplacement, une copie ou un interdit. Vous devez vous assurer, avec les interactions et les vues personnalisées, que vous conservez l'ensemble de comportements attendu que d'autres applications iOS 11, notamment les applications système, prennent en charge..
Un autre aspect important à considérer est de décider si votre session de glisser entraînera un déplacement ou une copie. En règle générale, Apple suggère que, lorsque vous travaillez au sein d'une même application, cela entraîne généralement un déplacement, alors qu'il est plus logique de copier l'élément de données lorsque vous faites glisser une application à la fois. Bien qu’il y ait des exceptions, bien entendu, le principe sous-jacent est que cela devrait avoir un sens pour l’utilisateur, et ce à quoi ils s’attendent devrait se passer..
Vous devriez également penser en termes de sources et de destinations, et s'il est logique de faire glisser quelque chose ou non.
Jetons un coup d'oeil à certains des utilitaires système d'Apple. Notes, par exemple, vous permet de sélectionner et de faire glisser du contenu textuel vers d'autres emplacements de l'application ou vers d'autres applications de l'iPad, via un écran partagé. L'application Rappels vous permet de déplacer des éléments de rappel d'une liste à une autre. Pensez en termes de fonctionnalité pour décider de la manière dont les utilisateurs utilisent votre contenu.
Apple recommande que tout le contenu modifiable prenne en charge l'acceptation du contenu supprimé, et que tout contenu sélectionnable accepte le contenu déplaçable, en plus de la copie et du collage pour ces types d'éléments. En exploitant les vues de texte et les champs de texte du système standard, vous obtiendrez une assistance pour le glisser-déposer immédiat..
Vous devez également prendre en charge le glisser-déposer multi-éléments, par opposition à un seul élément. Les utilisateurs peuvent utiliser plusieurs doigts pour sélectionner plusieurs éléments simultanément, ce qui permet d'empiler les éléments sélectionnés dans un groupe et de les déposer dans les destinations souhaitées. Un exemple de cela en action consiste à sélectionner plusieurs images dans l'application Photos ou plusieurs fichiers dans l'application Fichiers..
Une dernière ligne directrice consiste à donner aux utilisateurs la possibilité d’annuler une action ou d’annuler une suppression. Les utilisateurs sont depuis longtemps habitués à l'idée d'annuler une action dans la plupart des applications populaires, et le glisser-déposer ne devrait pas faire exception à la règle. Les utilisateurs doivent avoir la confiance nécessaire pour pouvoir lancer un glisser-déposer et pouvoir inverser cette action s'ils déposent l'élément dans la mauvaise destination..
Au-delà de ce que nous avons vu, il existe de nombreuses autres recommandations relatives aux pratiques de glisser-déposer, notamment la prise en charge des indices visuels perdus, l'affichage des actions abandonnées ayant échoué et les indicateurs de progression pour les sessions de glisser non instantanées, telles que les transferts de données. Consultez les directives d'interface humaine iOS d'Apple sur le glisser-déposer pour obtenir la liste complète des meilleures pratiques..
Dans ce didacticiel, vous avez appris à enrichir vos applications iOS avec le glisser-déposer, grâce à iOS 11. En chemin, nous avons expliqué comment activer les vues personnalisées et les vues sous forme de tableaux en tant que sources de glisser et destinations de dépôt..
Dans le cadre de l'évolution d'iOS vers une interface utilisateur davantage axée sur les gestes, il ne fait aucun doute que le glisser-déposer deviendra rapidement une fonctionnalité attendue par les utilisateurs à l'échelle du système. De ce fait, toutes les applications tierces doivent également être conformes. Et tout aussi important que la mise en œuvre du glisser-déposer, vous devez le mettre en œuvre correctement, de sorte qu'il devienne une seconde nature pour les utilisateurs, englobant simplicité et fonctionnalité..
Et pendant que vous êtes ici, consultez certains de nos autres articles sur le développement d'applications iOS!