撤消和重做 NSPanGestureRecognizer 进行的 NSView 对象移动
Undoing and Redoing NSView Object Move Made by NSPanGestureRecognizer
我正在开发一个示例桌面应用程序来测试 UndoManager
class,我不经常使用它。反正下面是思路。
- 我创建了两个
NSView
子视图对象(红色和蓝色)。它们被添加到 IBOutlet 连接的 NSView
对象 (panView)。
- 我为这两个子视图对象添加了平移手势(
NSPanGestureRecognizer
)。
- 当用户移动任一子视图对象时,应用程序调用撤消管理器。
以下是我从上到下的全部代码。
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
}
}
}
}
有效。它不会崩溃。只是我必须按两次 Command + Z 才能撤消移动。我必须按 Command + Shift + Z 两次才能重做移动。所以我不知道这个停顿是从哪里来的。我做错了什么,你知道吗?谢谢。
撤消注册了两次:在 redoMoveObject
中的 sender.state == .began
和 undoMoveObject(dict)
中的 sender.state == .ended
。 undoMoveObject
和redoMoveObject
做同样的事情,互相注册,它们可以合并成一个注册自己的函数。
示例:
// 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
}
}
我正在开发一个示例桌面应用程序来测试 UndoManager
class,我不经常使用它。反正下面是思路。
- 我创建了两个
NSView
子视图对象(红色和蓝色)。它们被添加到 IBOutlet 连接的NSView
对象 (panView)。 - 我为这两个子视图对象添加了平移手势(
NSPanGestureRecognizer
)。 - 当用户移动任一子视图对象时,应用程序调用撤消管理器。
以下是我从上到下的全部代码。
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
}
}
}
}
有效。它不会崩溃。只是我必须按两次 Command + Z 才能撤消移动。我必须按 Command + Shift + Z 两次才能重做移动。所以我不知道这个停顿是从哪里来的。我做错了什么,你知道吗?谢谢。
撤消注册了两次:在 redoMoveObject
中的 sender.state == .began
和 undoMoveObject(dict)
中的 sender.state == .ended
。 undoMoveObject
和redoMoveObject
做同样的事情,互相注册,它们可以合并成一个注册自己的函数。
示例:
// 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
}
}