【问题标题】:What is the correct way to close a popover?关闭弹出窗口的正确方法是什么?
【发布时间】:2017-07-15 09:49:21
【问题描述】:

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

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

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

view.window?.close()

弹出框关闭,但我意识到它仍然在内存中,deinit() 永远不会被调用,NSApp.windows 计数会增加,而如果我通过按退出键或在它外部单击将其关闭,deinit 调用并且窗口数没有增加。

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

(斯威夫特 3,Xcode 8)

【问题讨论】:

    标签: swift macos popover dismiss nspopover


    【解决方案1】:

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

    【讨论】:

    • 我们在 NSViewController 的子类中,它没有这个方法。它的观点也没有。该窗口是一个 NSPopoverWindow,我找不到它的文档。
    • 您在某处声明了您调用 showRelativeTo:of:preferredEdge:popover 变量。这就是您需要调用performClose 的对象。如果您必须从嵌入在弹出窗口中的 NSViewController 中的代码中关闭它,您必须找出一种访问该对象的方法。也许创建一个 PopoverViewController 作为基类,它具有您在显示之前设置的 popover 属性。或者使用委托模式。或者使用通知(虽然更喜欢其他解决方案,因为这会很快导致意大利面条代码)。
    • 我刚刚尝试了第一个想法,添加了对弹出框的引用并在调用 show(...) 之前进行了初始化。然后在“完成”方法中调用popover.close()。它可以正常关闭,但从不取消。
    • @AlexT 你是如何声明popover 的?如果你持有对它的强引用,它不会 deinit。
    • 本地声明。我添加了一个取消按钮,没有附加任何代码。弹出框消失了,但 deinit 没有被调用,奇怪的是,窗口不再在 NSApp.windows 数组中,所以它挂在内存中未被引用。
    【解决方案2】:

    感谢-DrummerB 的关注。我花了一些时间来制作一个我可能会发送给您的简单测试应用程序,当然它不像我的那样是基于文档的应用程序,这似乎使问题变得模糊不清。我打开弹出框的方式是基于我最近阅读的一个示例,但现在找不到或者我会警告人们。事情是这样的:

    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 可靠地发生了。

    【讨论】:

    • 在几个星期后回到这个关于其他问题的;这个答案不太正确。 deinit 发生正确,但NSApp.windows 报告的窗口列表不断增长,但如果您使用转义键关闭弹出窗口,则不会。请帮忙!
    【解决方案3】:

    我建议做以下事情:

    定义这样的协议

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

    【讨论】:

      猜你喜欢
      • 2015-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多