Undoing and Redoing NSView Object Move Made by NSPanGestureRecognizer

二次信任 提交于 2020-01-16 08:36:32

问题


I am working on a sample desktop application to test the UndoManager class, which I don't often get to use. Anyway, the following is the idea.

  1. I create two NSView sub view objects (red & blue). They are added to an IBOutlet-connected NSView object (panView).
  2. I add the pan gesture (NSPanGestureRecognizer) to these two sub view objects.
  3. The applications calls the undo manager when the user moves either sub view object.

The following is my entire code from top to bottom.

 import Cocoa

 class ViewController: NSViewController {
     // MARK: - Variables
     var myView1 = NSView()
     var myView2 = NSView()
     var uuid1 = String()
     var uuid2 = String()


     // MARK: - IBOutlet
     @IBOutlet weak var panView: NSView!


     // MARK: - Life cycle
     override func viewDidLoad() {
         super.viewDidLoad()

         /* myView */
         uuid1 = UUID().uuidString
         myView1 = NSView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: 100, height: 100)))
         myView1.identifier = NSUserInterfaceItemIdentifier(rawValue: uuid1)
         myView1.wantsLayer = true
         if let myLayer1 = myView1.layer {
          myLayer1.backgroundColor = NSColor.red.cgColor
         }
         panView.addSubview(myView1)

         uuid2 = UUID().uuidString
         myView2 = NSView(frame: CGRect(origin: CGPoint(x: 400, y: 200), size: CGSize(width: 100, height: 100)))
         myView2.identifier = NSUserInterfaceItemIdentifier(rawValue: uuid2)
         myView2.wantsLayer = true
         if let myLayer2 = myView2.layer {
          myLayer2.backgroundColor = NSColor.blue.cgColor
         }
         panView.addSubview(myView2)

         /* pangesture */
         let panRecognizer1 = NSPanGestureRecognizer.init(target: self, action: #selector(panPictureView(_:)))
         let panRecognizer2 = NSPanGestureRecognizer.init(target: self, action: #selector(panPictureView(_:)))
         myView1.addGestureRecognizer(panRecognizer1)
         myView2.addGestureRecognizer(panRecognizer2)
     }         

     // MARK: - Pan gesture
     @objc func panPictureView(_ sender: NSPanGestureRecognizer) {
         let translation = sender.translation(in: self.view)
         if let movingObject = sender.view {
          let newPosition = CGPoint(x: movingObject.frame.origin.x + translation.x, y: movingObject.frame.origin.y + translation.y)
          movingObject.setFrameOrigin(newPosition)
          sender.setTranslation(CGPoint.zero, in: self.view)
          if sender.state == .began {
              if let rawID = sender.view?.identifier?.rawValue {
                  let dict = ["point": movingObject.frame.origin, "rawID": rawID] as [String : Any]
                  redoMoveObject(dict)
              }
          }
          else if sender.state == .ended {
              if let rawID = sender.view?.identifier?.rawValue {
                  let dict = ["point": movingObject.frame.origin, "rawID": rawID] as [String : Any]
                  undoMoveObject(dict)
              }
          }
         }
     }


     // MARK: - Undoing move
     @objc func undoMoveObject(_ newObject: [String : Any]) {
         undoManager?.registerUndo(withTarget: self, selector: #selector(redoMoveObject(_:)), object: newObject)
         undoManager?.setActionName("Move Object")
         if let point = newObject["point"] as? CGPoint, let rawID = newObject["rawID"] as? String {
          if rawID == uuid1 {
              myView1.frame.origin = point
          }
          else {
              myView2.frame.origin = point
          }
         }
     }

     @objc func redoMoveObject(_ newObject: [String : Any]) {
         undoManager?.registerUndo(withTarget: self, selector: #selector(undoMoveObject(_:)), object: newObject)
         undoManager?.setActionName("Move Object")
         if let point = newObject["point"] as? CGPoint, let rawID = newObject["rawID"] as? String {
          if rawID == uuid1 {
              myView1.frame.origin = point
          }
          else {
              myView2.frame.origin = point
          }
         }
     }
 }

It works. It doesn't crash. It's just that I have to press Command + Z twice to undo the move. And I have to press Command + Shift + Z twice to redo the move. So I don't know where this pause comes from. What am I doing wrong, do you know? Thanks.


回答1:


Undo is registered twice: at sender.state == .began in redoMoveObject and at sender.state == .ended in undoMoveObject(dict). undoMoveObject and redoMoveObject do the same thing and register each other, they can be merged into one function that registers itself.

Example:

// MARK: - Pan gesture
@objc func panPictureView(_ sender: NSPanGestureRecognizer) {
    if let movingObject = sender.view {
        if sender.state == .began { // register undo before first move
            if let rawID = movingObject.identifier?.rawValue {
                let dict = ["point": movingObject.frame.origin, "rawID": rawID] as [String : Any]
                undoMoveObject(dict)
            }
        }
        let translation = sender.translation(in: self.view)
        sender.setTranslation(CGPoint.zero, in: self.view)
        let newPosition = CGPoint(x: movingObject.frame.origin.x + translation.x, y: movingObject.frame.origin.y + translation.y)
        movingObject.setFrameOrigin(newPosition)
    }
}

// MARK: - Undoing move
@objc func undoMoveObject(_ newObject: [String : Any]) {
    if let point = newObject["point"] as? CGPoint, let rawID = newObject["rawID"] as? String {
        var movingObject: NSView
        if rawID == uuid1 {
            movingObject = myView1
        }
        else {
            movingObject = myView2
        }
        // register current frame origin for redo/undo
        let dict = ["point": movingObject.frame.origin, "rawID": rawID] as [String : Any]
        undoManager?.registerUndo(withTarget: self, selector: #selector(undoMoveObject(_:)), object: dict)
        undoManager?.setActionName("Move Object")
        // undo/redo
        movingObject.frame.origin = point
    }
}


来源:https://stackoverflow.com/questions/59610266/undoing-and-redoing-nsview-object-move-made-by-nspangesturerecognizer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!