Je travaille sur un projet qui inclut un outil d'annotation permettant aux utilisateurs de «dessiner» des documents avec des gestes au doigt ou avec un crayon. Naturellement, je suis désireux de mettre en œuvre annuler/refaire pour les chemins tracés.Implémentation de la fonction de restauration avec UndoManager dans l'application de dessin Swift 3
Ma mise en œuvre pour l'application de dessin est relativement conventionnelle. Ce que l'utilisateur voit sur l'écran est la combinaison d'une image bitmap mise en cache (un instantané de tous les chemins qui ont été dessinés avant l'actuel) avec un rendu "en direct" du chemin courant (un UIBezierPath). Lorsque touchesEnded est déclenchée, le nouveau chemin est ajouté au bitmap.
J'ai été en mesure d'implémenter l'annulation avec relativement peu de problèmes. J'ai créé un undoManager standard pour la classe:
let myUndoManager : UndoManager = {
let mUM : UndoManager = UndoManager()
mUM.levelsOfUndo = 6
return mUM
}()
La fonction qui est appelée à la fin de touchesEnded pour rendre le nouveau chemin en cache est appelé DrawBitmap. Au début de cette fonction, en supposant qu'il existe un chemin précédent en cache et avant de dessiner le nouveau, j'inscrire l'action undo suivante avec le gestionnaire de undo:
let previousCachedPath : UIImage = self.cachedPath
self.myUndoManager.registerUndo(withTarget: self, selector: #selector(self.setBitmap(_:)), object: previousCachedPath)
SetBitmap (_ previousCachedPath: UIImage) est une fonction qui réinitialise le bitmap affiché à l'image fournie.
J'ai des boutons annuler/rétablir liés respectivement aux méthodes undo() et redo(). Mis à part une certaine logique dictant quand ces boutons devraient être actifs (pour s'assurer que vous ne pouvez pas appuyer sur Annuler quand rien n'a été dessiné, etc), ils appellent simplement myUndoManager.undo() et myUndoManager.redo() respectivement:
func undo() -> Void {
guard self.myUndoManager.canUndo else { return }
self.myUndoManager.undo()
if !self.redoButton.isEnabled {
self.redoButton.isEnabled = true
}
if !self.myUndoManager.canUndo {
self.undoButton.isEnabled = false
}
self.setNeedsDisplay()
}
func redo() -> Void {
guard self.myUndoManager.canRedo else { return }
self.myUndoManager.redo()
if !self.undoButton.isEnabled {
self.undoButton.isEnabled = true
}
if !self.myUndoManager.canRedo {
self.redoButton.isEnabled = false
}
self.setNeedsDisplay()
}
Comme je l'ai mentionné, annuler fonctionne parfaitement avec les six niveaux spécifiés d'annulation. Cependant, il me manque clairement quelque chose avec refaire. Mon espoir initial était que le undoManager transfère automatiquement les tâches d'annulation de la pile d'annulation à la pile de rétablissement quand l'annulation est appelée, mais cela ne se produit manifestement pas.
Je l'ai déjà recherché des réponses, et je pense que le plus proche de ce que je dois peut-être utiliser récursion mutuelle par:
Using NSUndoManager, how to register undos using Swift closures
Cependant, je n'ai pas été en mesure de faire ce travail. Toute aide donc appréciée!
L'astuce est que vous devriez avoir juste la méthode _one_ et à la fois annuler et refaire l'appeler. L'annulation d'un rétablissement est une annulation. L'annulation d'une annulation est un refaire. Cela pourrait vous aider à lire le chapitre de mon livre: http://www.apeth.com/iOSBook/ch39.html – matt
Merci - Je vais vous lire ce matin et vous laisser savoir comment je m'entends! – Sparky
@matt Merci beaucoup, a parfaitement fonctionné. Comme vous l'avez suggéré, j'ai tout combiné en une seule méthode, dans ce cas setBitmap (_ :). Je l'ai eu à travailler avec l'une des méthodes que vous avez décrites. J'ai posté ma solution au cas où cela aiderait les autres. – Sparky