Как правильно закрыть поповер?

В моем подклассе NSDocument я создаю экземпляр NSPopover, с .semitransient поведение, и показать это:

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

popover объявлен локально. Метод кнопки в контроллере поповера вызывает:

view.window?.close()

Поповер закрывается, но я осознал, что он остается в памяти, deinit() никогда не вызывается и NSApp.windows количество увеличивается, тогда как если я отклоняю его, нажимая escape или щелкая снаружи, deinit вызывается и количество окон не увеличивается.

Если я установлю окно .isReleasedWhenClosed в trueколичество окон не увеличивается, но deinit по-прежнему не вызывается.

(Swift 3, Xcode 8)

3 ответа

Вы должны вызвать executeClose (или закрыть) для всплывающего окна, а не окна.

Спасибо -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 бывает надежно.

Я бы предложил сделать следующее:

Определите такой протокол

      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
    }
}
Другие вопросы по тегам