How can you implement the NSDocument method -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: in Swift?

后端 未结 4 1415
粉色の甜心
粉色の甜心 2021-01-13 07:25

In my application, a NSDocument subclass mission-critical hardware – users really don’t want to close a document by accident! So, I’ve implemented canClos

相关标签:
4条回答
  • 2021-01-13 08:05

    Here is a Swift solution to this issue that I received from Apple Developer Technical Support:

    override func canCloseDocumentWithDelegate(delegate: AnyObject, shouldCloseSelector: Selector, contextInfo: UnsafeMutablePointer<Void>) {
        super.canCloseDocumentWithDelegate(self, shouldCloseSelector: "document:shouldClose:contextInfo:", contextInfo: contextInfo)
    }
    
    func document(doc:NSDocument, shouldClose:Bool, contextInfo:UnsafeMutablePointer<Void>) {
        if shouldClose {
            // <Your clean-up code>
            doc.close()
        }
    }
    
    0 讨论(0)
  • 2021-01-13 08:07

    You can solve this with some low level runtime functions:

    override func canCloseDocumentWithDelegate(delegate: AnyObject, shouldCloseSelector: Selector, contextInfo: UnsafeMutablePointer<Void>) {
    
        let allowed = true // ...or false. Add your logic here.
    
        let Class: AnyClass = object_getClass(delegate)
        let method = class_getMethodImplementation(Class, shouldCloseSelector)
    
        typealias signature = @convention(c) (AnyObject, Selector, AnyObject, Bool, UnsafeMutablePointer<Void>) -> Void
        let function = unsafeBitCast(method, signature.self)
    
        function(delegate, shouldCloseSelector, self, allowed, contextInfo)
    }
    

    If you need to move this behaviour to another method (eg. after a sheet gets confirmation from the user), simply store the delegate and shouldCloseSelector in properties so you can access them later.

    0 讨论(0)
  • 2021-01-13 08:08

    So, my current solution to this, is to keep using Objective-C to perform the NSInvocation. The NSDocument subclass is written in Swift, and calls an Objective-C category to do this bit of work.

    Since NSInvocation does not exist in Swift, I really don’t see any other way.

    - (void)respondToCanClose:(BOOL)shouldClose delegate:(id)delegate selector:(SEL)shouldCloseSelector contextInfo:(void *)contextInfo
    {
        NSDocument *doc = self;
    
        NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[delegate methodSignatureForSelector:shouldCloseSelector]];
        invocation.target = delegate;
        invocation.selector = shouldCloseSelector;
        [invocation setArgument:&doc atIndex:2]; // Note index starts from 2 - 0 & 1 are self & selector
        [invocation setArgument:&shouldClose atIndex:3];
        [invocation setArgument:&contextInfo atIndex:4];
    
        [invocation invoke];
    }
    

    You can see my sample project: https://github.com/DouglasHeriot/canCloseDocumentWithDelegate

    Another option is to use Objective-C to wrap around objc_msgSend, which is also unavailable in Swift. http://www.cocoabuilder.com/archive/cocoa/87293-how-does-canclosedocumentwithdelegate-work.html#87295

    0 讨论(0)
  • 2021-01-13 08:09

    At least as of Swift 4.1, you can do something like:

    // Application Logic
    myDocument.canClose(
        withDelegate: self,
        shouldClose: #selector(MyClass.document(_:_:_:)),
        contextInfo: nil)
    
    ...
    
    // Handler
    @objc
    private func document(_ doc: NSDocument, _ shouldClose: Bool, _ contextInfo: UnsafeMutableRawPointer) {
        ...
    }
    
    0 讨论(0)
提交回复
热议问题