2016-09-18 4 views
4

Je migre un projet Mac OS X Xcode 7/Swift 2.2 vers Xcode 8/Swift 3, et j'ai rencontré un problème en utilisant undoManager dans ma classe de contrôleur de vue, MyViewController, qui a une fonction Annuler.Utilisation de NSUndoManager et .prepare (withInvocationTarget :) dans Swift 3

Dans Xcode 7/Swift 2.2, cela a fonctionné très bien:

undoManager?.prepareWithInvocationTarget(self).undo(data, moreData: moreData) 
undoManager?.setActionName("Change Data) 

Dans Xcode 8/Swift 3, en utilisant le modèle recommandé de https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html

cela devrait être changé:

if let target = undoManager?.prepare(withInvocationTarget: self) as? MyViewController { 
    target.undo(data, moreData: moreData) 
    undoManager?. setActionName("Change Data") 
} 

Cependant, le basculement vers MyViewController échoue toujours et l'opération d'annulation n'est pas enregistrée.

Ai-je manqué quelque chose d'évident ici, ou est-ce un bug?

Répondre

7

prepareWithInvocationTarget(_:) (ou prepare(withInvocationTarget:) dans Swift 3) crée un objet proxy caché, avec lequel Swift 3 runtime ne peut pas fonctionner correctement.

(Vous pouvez appeler un bug, et envoyer un rapport de bug.)

Pour atteindre votre but, ne pouvez-vous utiliser registerUndo(withTarget:handler:)?

undoManager?.registerUndo(withTarget: self) {targetSelf in 
    targetSelf.undo(data, moreData: moreData) 
} 
+0

Merci. J'ai joué avec ça aussi. Malheureusement, il n'est pas compatible avec OS10.10, mais il est peut-être temps de passer à autre chose. – jbaraga

+1

Solution pour OS 10.10: utiliser 'registerUndo (avec Target: selector: object:)'. Pas de problème pour sauvegarder une seule valeur. Pour sauvegarder plusieurs valeurs, je les empaquette dans un dictionnaire et je l'utilise pour le paramètre "objet". Pour l'opération d'annulation, je les décompresse du dictionnaire. – jbaraga

+0

@jbaraga, merci pour les rapports. Mais pourquoi ne pas l'afficher comme une autre réponse? Cela peut être utile pour les développeurs qui ont besoin de travailler avec 10.10. – OOPer

1

Solution pour la compatibilité avec les OS 10.10: utilisez registerUndo(with Target: selector: object:). Pas de problème pour sauvegarder une seule valeur. Pour sauvegarder plusieurs valeurs, je les empaquette dans un dictionnaire et je l'utilise pour le paramètre "objet". Pour l'opération d'annulation, je les décompresse du dictionnaire, puis appelle la méthode d'annulation OS10.11 + avec ces valeurs.

+0

Merci. En incluant le code de cas "valeurs multiples", plus de personnes peuvent facilement trouver votre réponse utile. S'il vous plaît envisager de prendre du temps pour cela. – OOPer

2

J'ai eu le même problème et je n'étais pas prêt à abandonner la prise en charge d'iOS 8 et macOS 10.10 ou à revenir à Swift 2.3. La syntaxe registerUndo(withTarget:handler) est bien, donc je fondamentalement juste roulé ma propre version de ce:

/// An extension to undo manager that adds closure based 
/// handling to OS versions where it is not available. 
extension UndoManager 
{ 
    /// Registers an undo operation using a closure. Behaves in the same wasy as 
    /// `registerUndo(withTarget:handler)` but it compatible with older OS versions. 
    func compatibleRegisterUndo<TargetType : AnyObject>(withTarget target: TargetType, handler: @escaping (TargetType) ->()) 
    { 
     if #available(iOS 9.0, macOS 10.11, *) 
     { 
      self.registerUndo(withTarget: target, handler: handler) 
     } 
     else 
     { 
      let operation = BlockOperation { 
       handler(target) 
      } 
      self.registerUndo(withTarget: self, selector: #selector(UndoManager.performUndo(operation:)), object: operation) 
     } 
    } 

    /// Performs an undo operation after it has been registered 
    /// by `compatibleRegisterUndo`. Should not be called directly. 
    func performUndo(operation: Operation) 
    { 
     operation.start() 
    } 
} 

Espérons qu'il est utile à quelqu'un d'autre aussi.