You probably know that, to show an NSPopover, we must set its contentViewController property to an NSViewController.
And you probably know that, to detach a popover into a custom window, we must implement detachableWindowForPopover: in the popover’s delegate, and the detached window cannot share the popover’s contentViewController.
So the logical approach is to create an NSViewController subclass to manage the popover’s content and the detached window’s content, and create separate instances of this subclass for the popover and for the detached window.
The problem arises when we create an NSWindow, set its contentViewController to a new instance, and then return the NSWindow from detachableWindowForPopover:. AppKit temporarily replaces the new window’s contentView. It restores the window’s contentView but does not restore the window’s contentViewController. The original contentViewController of the new window is lost.
I will attach a test project. To reproduce the problem in the test project:
Launch the test app.
Click the “Show Popover” button.
Drag the popover to detach it.
As soon as we begin the drag, we see a message in the main window showing the description of the newly created ContentViewController.
When we release the drag, the detached window shows a message put there by the detached window’s ContentViewController when it was destroyed.
That ContentViewController should not have been destroyed!
Apparently bugreport.apple.com now limits the description field to 3000 characters, so I will continue this report in comments…
Checking the console output, we can see what happened: our detachableWindowForPopover set the window’s contentViewController, which also set the window’s contentView:
2019-04-23 16:00:54.127720-0500 popover-bug[3575:200709] detachableWindowForPopover: initialized <MyPanel: 0x600003e06c00> with <ContentViewController: 0x600002c27a00> and <NSView: 0x600003306080>
Then AppKit replaced the new window’s contentView by sending it setContentView:, which set the new window’s contentViewController to nil:
I am experimenting with a workaround for this bug. The workaround is, in ContentViewController's viewWillAppear method, to set self.view.window.contentViewController = self (if it's not already self). This works in the test program, but it seems very fishy to change the window's contentViewController in viewWillAppear. You can enable this workaround in the test program by turning on the “Enable Workaround” checkbox.
Please restore the detached window's contentViewController so we don't have to test fishy workarounds.
Description
Dear AppKitterinos,
You probably know that, to show an NSPopover, we must set its contentViewController property to an NSViewController.
And you probably know that, to detach a popover into a custom window, we must implement detachableWindowForPopover: in the popover’s delegate, and the detached window cannot share the popover’s contentViewController.
So the logical approach is to create an NSViewController subclass to manage the popover’s content and the detached window’s content, and create separate instances of this subclass for the popover and for the detached window.
The problem arises when we create an NSWindow, set its contentViewController to a new instance, and then return the NSWindow from detachableWindowForPopover:. AppKit temporarily replaces the new window’s contentView. It restores the window’s contentView but does not restore the window’s contentViewController. The original contentViewController of the new window is lost.
I will attach a test project. To reproduce the problem in the test project:
That ContentViewController should not have been destroyed!
Apparently bugreport.apple.com now limits the description field to 3000 characters, so I will continue this report in comments…
Checking the console output, we can see what happened: our detachableWindowForPopover set the window’s contentViewController, which also set the window’s contentView:
Then AppKit replaced the new window’s contentView by sending it setContentView:, which set the new window’s contentViewController to nil:
Later, AppKit restored the new window's contentView, but it did not restore the contentViewController:
I am experimenting with a workaround for this bug. The workaround is, in ContentViewController's viewWillAppear method, to set self.view.window.contentViewController = self (if it's not already self). This works in the test program, but it seems very fishy to change the window's contentViewController in viewWillAppear. You can enable this workaround in the test program by turning on the “Enable Workaround” checkbox.
Please restore the detached window's contentViewController so we don't have to test fishy workarounds.
Thanks, Rob
- Product Version: 10.14.5 Beta (18F118d) Created: 2019-04-23T21:49:05.923817 Originated: 2019-04-23T00:00:00 Open Radar Link: http://www.openradar.me/50144082