关闭弹出窗口的正确方法是什么?

时间:2017-02-24 14:08:43

标签: swift macos popover dismiss nspopover

在我的NSDocument子类中,我实例化一个具有.semitransient行为的NSPopover,并显示它:

popover.show(relativeTo: rect, of: sender, preferredEdge: .maxX)

popover在本地声明。弹出控制器中的按钮方法调用:

view.window?.close()

popover关闭,但我已经意识到它仍在内存中,deinit()从未被调用且NSApp.windows计数增加,而如果我通过按下escape或点击它来解除它,{ {1}} 调用,并且窗口计数不会增加。

如果我将窗口的deinit设置为.isReleasedWhenClosed,则窗口计数不会增加,但仍然不会调用deinit。

(Swift 3,Xcode 8)

3 个答案:

答案 0 :(得分:0)

你必须在弹出窗口而不是窗口上调用performClose(或close)。

答案 1 :(得分:0)

感谢-DrummerB感兴趣。我花了一些时间来制作一个我可能发送给你的简单测试应用程序,当然它不像我的那样是一个基于文档的应用程序,这似乎使问题蒙上阴影。我打开popover的方式基于我最近阅读的一个例子,但现在无法找到或者我警告人们。它是这样的:

let popover = NSPopover
let controller = MyPopover(...)! // my convenience init for NSViewController descendant
popover.controller = controller
popover.behaviour = .semitransient // and setting other properties
popover.show(relativeTo: rect, of: sender, preferredEdge: .maxX)

以下是我遇到的改进方式:

let controller = MyPopover(...)! // descendant of NSViewController
controller.presentViewController(controller, 
    asPopoverRelativeTo: rect, of: sender, preferredEdge: .maxX,
    behavior: .semitransient) // sender was a NSTable

在视图控制器中,'完成'按钮的动作只是:

dismissViewController(self)
之前从未奏过的

现在我发现应用程序的窗口列表没有增长,控制器的deinit可靠地发生。

答案 2 :(得分:0)

我建议您执行以下操作:

定义一个这样的协议

protocol PopoverManager {
    func dismissPopover(_ sender: Any)
}

在您的 popoverViewController(在本例中,我们将过滤视图控制器显示为弹出框)中,为 popoverManager 添加一个变量,如下所示

/// Filter shown as a NSPopover()
class FilterViewController: NSViewController {
    
    // Delegate
    var popoverManager: PopoverManager?
        
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do view setup here.
        
    }
    
    // Bind this to the close button or action on your popover view controller
    @IBAction func closeAction(_ sender: Any) {
        self.popoverManager?.dismissPopover(sender)
    }

   ...

}

现在在您的 viewController 中,您可以通过添加这样的扩展来显示弹出框

extension MainViewController: NSPopoverDelegate, PopoverManager {
    @IBAction func setFilter(_ sender: AnyObject) {
        self.showFilterPopover(sender)
    }
    
    func showFilterPopover(_ sender: AnyObject) {
        let storyboard = NSStoryboard(name: "Filter", bundle: nil)
        guard let controller = storyboard.instantiateController(withIdentifier: "FilterViewController") as? FilterViewController else {
            return
        }
        // Set the delegate to self so we can dismiss the popover from the popover view controller itself.
        controller.popoverManager = self
        
        self.popover = NSPopover()
        self.popover.delegate = self
        self.popover.contentViewController = controller
        self.popover.contentSize = controller.view.frame.size
        
        self.popover.behavior = .applicationDefined
        self.popover.animates = true
        self.popover.show(relativeTo: sender.bounds, of: sender as! NSView, preferredEdge: NSRectEdge.maxY)
        
    }
    
    func dismissPopover(_ sender: Any) {
        self.popover?.performClose(sender)
        // If you don't want to reuse it
        self.popover = nil
    }
}