How do I register UndoManager in Swift?

后端 未结 7 979
温柔的废话
温柔的废话 2020-12-09 19:43

How do I use UndoManager (previously NSUndoManager) in Swift?

Here\'s an Objective-C example I\'ve tried to replicate:

[[un         


        
7条回答
  •  不知归路
    2020-12-09 20:23

    I too have done a bit of reading and came up with the following: I have 2 tableViews, source by a dictionary and array controller for playlists and its items respectively, which I'm adding to the Helium 3 project on GitHub (not there yet); here's a preview:

    dynamic var playlists = Dictionary()
    dynamic var playCache = Dictionary()
    
    //  MARK:- Undo keys to watch for undo: dictionary(list) and play item
    var listIvars : [String] {
        get {
            return ["key", "value"]
        }
    }
    var itemIvars : [String] {
        get {
            return ["name", "temp", "time", "rank", "rect", "label", "hover", "alpha", "trans"]
        }
    }
    
    internal func observe(_ item: AnyObject, keyArray keys: [String], observing state: Bool) {
        switch state {
        case true:
            for keyPath in keys {
                item.addObserver(self, forKeyPath: keyPath, options: [.old,.new], context: nil)
            }
            break
        case false:
            for keyPath in keys {
                item.removeObserver(self, forKeyPath: keyPath)
            }
        }
    }
    
    //  Start or forget observing any changes
    internal func observing(_ state: Bool) {
        for dict in playlists {
            let items: [PlayItem] = dict.value as! [PlayItem]
            self.observe(dict as AnyObject, keyArray: listIvars, observing: state)
            for item in items {
                self.observe(item, keyArray: itemIvars, observing: state)
            }
        }
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if let undo = self.undoManager {
            let oldValue = change?[NSKeyValueChangeKey(rawValue: "old")]
            let newValue = change?[NSKeyValueChangeKey(rawValue: "new")]
    
            undo.registerUndo(withTarget: self, handler: {[oldVals = ["key": keyPath!, "old": oldValue as Any] as [String : Any]] (PlaylistViewController) -> () in
    
                (object as AnyObject).setValue(oldVals["old"], forKey: oldVals["key"] as! String)
                if !undo.isUndoing {
                    undo.setActionName(String.init(format: "Edit %@", keyPath!))
                }
            })
            Swift.print(String.init(format: "%@ %@ -> %@", keyPath!, oldValue as! CVarArg, newValue as! CVarArg))
        }
    }
    override func viewWillAppear() {
        //  Start observing any changes
        observing(true)
    }
    
    override func viewDidDisappear() {
        //  Stop observing any changes
        observing(false)
    }
    
    //  "List" items are controller objects - NSDictionaryControllerKeyValuePair
    internal func addList(_ item: NSDictionaryControllerKeyValuePair, atIndex index: Int) {
        if let undo = self.undoManager {
            undo.registerUndo(withTarget: self, handler: {[oldVals = ["item": item, "index": index] as [String : Any]] (PlaylistViewController) -> () in
                self.removeList(oldVals["item"] as! NSDictionaryControllerKeyValuePair, atIndex: oldVals["index"] as! Int)
                if !undo.isUndoing {
                    undo.setActionName("Add PlayList")
                }
            })
        }
        observe(item, keyArray: listIvars, observing: true)
        playlistArrayController.insert(item, atArrangedObjectIndex: index)
    
    
        DispatchQueue.main.async {
            self.playlistTableView.scrollRowToVisible(index)
        }
    }
    internal func removeList(_ item: NSDictionaryControllerKeyValuePair, atIndex index: Int) {
        if let undo = self.undoManager {
            undo.prepare(withInvocationTarget: self.addList(item, atIndex: index))
            if !undo.isUndoing {
                undo.setActionName("Remove PlayList")
            }
        }
        if let undo = self.undoManager {
            undo.registerUndo(withTarget: self, handler: {[oldVals = ["item": item, "index": index] as [String : Any]] (PlaylistViewController) -> () in
                self.addList(oldVals["item"] as! NSDictionaryControllerKeyValuePair, atIndex: oldVals["index"] as! Int)
                if !undo.isUndoing {
                    undo.setActionName("Remove PlayList")
                }
            })
        }
        observe(item, keyArray: listIvars, observing: false)
        playlistArrayController.removeObject(item)
    
        DispatchQueue.main.async {
            self.playlistTableView.scrollRowToVisible(index)
        }
    }
    

    "List" items are NSDictionaryControllerKeyValuePair for the NSDictionaryController.

    The "item" handling is a bit more complicated but this should get you going. Each time a list or item is added/removed the proper the add|remove method is called. Then you start observing as new items appear and forget as they're removed, this also observes each object's ivars for changes.

    Enjoy.

提交回复
热议问题