撤销和重做由NSPanGestureRecognizer进行的NSView对象移动。

huangapple go评论82阅读模式
英文:

Undoing and Redoing NSView Object Move Made by NSPanGestureRecognizer

问题

我正在开发一个示例桌面应用程序,用于测试UndoManager类,这是我不经常使用的。以下是我的想法。

  1. 我创建了两个NSView子视图对象(红色和蓝色)。它们被添加到一个IBOutlet连接的NSView对象(panView)中。
  2. 我为这两个子视图对象添加了平移手势(NSPanGestureRecognizer)。
  3. 当用户移动任一子视图对象时,应用程序调用撤销管理器。

以下是我的整个代码,从上到下。

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]
                    undoMoveObject(dict)
                }
            }
            else if sender.state == .ended {
                if let rawID = sender.view?.identifier?.rawValue {
                    let dict = ["point": movingObject.frame.origin, "rawID": rawID] as [String : Any]
                    redoMoveObject(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
            }
        }
    }
}

它可以工作,不会崩溃。只是我必须按两次Command + Z才能撤销移动操作,必须按两次Command + Shift + Z才能重做移动操作。所以我不知道这个暂停是从哪里来的。你知道我做错了什么吗?谢谢。

英文:

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
}
}
}
}

撤销和重做由NSPanGestureRecognizer进行的NSView对象移动。

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

得分: 0

撤销在redoMoveObject中的sender.state == .beganundoMoveObject(dict)中的sender.state == .ended都注册了两次。undoMoveObjectredoMoveObject执行相同的操作并相互注册,它们可以合并为一个函数来注册自身。

示例:

// MARK: - 拖动手势
@objc func panPictureView(_ sender: NSPanGestureRecognizer) {
    if let movingObject = sender.view {
        if sender.state == .began {    // 在第一次移动之前注册撤销
            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: - 撤销移动
@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
        }
        // 为重做/撤销注册当前框架原点
        let dict = ["point": movingObject.frame.origin, "rawID": rawID] as [String : Any]
        undoManager?.registerUndo(withTarget: self, selector: #selector(undoMoveObject(_:)), object: dict)
        undoManager?.setActionName("移动对象")
        // 撤销/重做
        movingObject.frame.origin = point
    }
}
英文:

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
}
}

huangapple
  • 本文由 发表于 2020年1月6日 18:17:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/59610266.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定