1

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 the NSOpenPanel 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 the NSView 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.

Rebecka
  • 1,213
  • 8
  • 14

0 Answers0