I am writing a settings popover for a Safari App Extension. This consists of an table view with an segmented control underneath containing Add, Remove, and Action templates. The latter reveals a menu with items to import and export settings.
Importing is via an NSOpenPanel
to which I have added an accessory view with a checkbox to replace the existing settings instead of adding to them.
The problem is, when the menu item is selected the accessory view appears to be behind the the modal NSOpenPanel
causing the following behaviours.
- The
NSView
briefly appears on the screen before theNSOpenPanel
does, then - The
NSOpenPanel
shows an empty space (of the correct size) where the accessory view should be, and - If you click on another application (or the desktop) to put Safari goes into the background then the accessory view does appear in the
NSOpenPanel
, except - If the other app overlaps the
NSOpenPanel
then theNSView
appears above that app, and you can click the checkbox to change it, then - Clicking on Safari to make it frontmost the
NSView
again disappears, however - Clicking the Options button twice of the
NSOpenPanel
to make the accessory view close then reopen fixes the problem.
I added a background colour to the NSView
to confirm the problem is with that and not the enclosed NSButton
.
To verify my code was correct I created a new application project with just a single button on its view, then copied my code into its action outlet. It worked correctly.
Then I created a new Safari App Extension project, and did the same on its popover, and experienced the same problem. I also tried moving the code outside of the view controller for the popover, but this made no difference. So it seems the problem is related to it being called from within a Safari App Extension.
Also, what I assume is a related problem, sometimes when the NSOpenPanel
is closed (whether by selecting a file or cancelling it) control is not passed back to the main Safari window. It just becomes unresponsive. You can move the window around the screen but the application itself, including menu bar, will not respond to any mouse or keyboard input. The only option is to Force Quit it. However this also affects the NSSavePanel
for exporting which does not have an accessory view.
I am using Xcode 11.3 on macOS 10.14.6 for Safari 13.0.4. This is the code:
var overwriteSettings: Bool = false
@objc func importSettingsCheckBox(_ button: NSButton) {
self.overwriteSettings = button.state == .on
}
@IBAction func importSettings(_ sender: Any) {
let openFileDialog = NSOpenPanel()
openFileDialog.title = "Choose a settings fileā¦"
openFileDialog.directoryURL = FileManager.default.homeDirectoryForCurrentUser
// openFileDialog.allowedFileTypes = ["json"]
// openFileDialog.allowsOtherFileTypes = true
let dialogWidth = openFileDialog.frame.width
let overwriteSettingsButton = NSButton()
overwriteSettingsButton.setButtonType(.switch)
overwriteSettingsButton.title = "Remove existing settings on import?"
overwriteSettingsButton.target = self
overwriteSettingsButton.action = #selector(importSettingsCheckBox(_:))
overwriteSettingsButton.sizeToFit()
let dialogWidth = openFileDialog.frame.width
let buttonHeight = overwriteSettingsButton.frame.height
overwriteSettingsButton.setFrameOrigin(NSMakePoint((dialogWidth - overwriteSettingsButton.frame.width) / 2, buttonHeight / 2))
let accessoryView = NSView(frame: NSMakeRect(0, 0, dialogWidth, buttonHeight * 2))
accessoryView.autoresizesSubviews = true
accessoryView.addSubview(overwriteSettingsButton)
openFileDialog.accessoryView = accessoryView
openFileDialog.isAccessoryViewDisclosed = true
if openFileDialog.runModal() == NSApplication.ModalResponse.OK {
if let result = openFileDialog.url {
NSLog("I want to open \(result.path), overwrite status: \(self.overwriteSettings)")
}
} else {
NSLog("No file selected")
}
return
}
(The commented out lines are because NSOpenPanel.allowsOtherFileTypes
is being ignored, but at least that constantly did not work in the standalone app test too!)
Can anyone explain this? I really want to have the settings within Safari rather than using the containing application to manage them.