Реализация команд меню «Экспорт/Импорт»?

У меня есть кнопки импорта (и экспорта), которые отлично работают в представлении.

var body: some View {
    Button(action: {
        isImporting = true
    }, label: {
        Label("Import", systemImage: "square.and.arrow.down")
    })
    .fileImporter(
        isPresented: $isImporting,
        allowedContentTypes: [UTType.log, UTType.text, UTType.xml, UTType.zip],
        allowsMultipleSelection: true
    ) { result in
        switch result {
        case let .success(files):
            files.forEach { file in
                guard file.startAccessingSecurityScopedResource() else { return }
                    .../...
                file.stopAccessingSecurityScopedResource()
            }
        case let .failure(error):
            print(error)
        }
    }
}

Но я также хочу, чтобы команды импорта/экспорта были доступны через меню «Файл» в приложении macOS.

struct ImportExportCommands: Commands {
    @MainActor
    var body: some Commands {
        CommandGroup(replacing: .importExport) {
            Section {
                ImportButton()
                    .modelContainer(mdContainer.sharedModelContainer)
                ExportButton()
                    .modelContainer(mdContainer.sharedModelContainer)
            }
        }
    }
}

Код действия выполняется, но диалоговое окно файла не появляется.

Я ничего не нашел по этому поводу, поэтому полагаю, что это очень глупая вещь.

Как я могу открыть диалоговое окно fileImporter/fileExporter на экране из команды меню?

Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
0
0
99
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Проблема, на мой взгляд, заключается в том, что вам сначала нужно открыть окно, чтобы модификатор .fileImporter заработал, но один из способов обойти это — использовать вместо него NSOpenPanel.

Что-то вроде этого:

struct ImportExportCommands: Commands {
    @Environment(\.openWindow) private var openWindows
    @MainActor
    var body: some Commands {
        CommandGroup(replacing: .importExport) {
            Section {
                Button("Import") {
                    selectImportFiles()
                }
            }
        }
    }

    private func selectImportFiles() {
        let openPanel = NSOpenPanel()
        openPanel.title = "Import files"
        openPanel.prompt = "Import"
        openPanel.canChooseFiles = true
        openPanel.allowsMultipleSelection = true
        openPanel.allowedContentTypes = [UTType.log, UTType.text, UTType.xml, UTType.zip]

        openPanel.begin { result in
            if result == .OK {
                for fileUrl in openPanel.urls {
                    print(fileUrl.absoluteString)
                }
            }
        }
    }
}

У меня была аналогичная проблема в приложении SwiftUI. Что мне помогло, так это добавить целенаправленную привязку к представлениям меню.

Для добавления фокусированной привязки необходимо выполнить следующие действия:

  • Создайте структуру ключей с фокусным значением для фокусированной привязки.
  • Создайте расширение структуры FocusedValues SwiftUI для созданного вами ключа целевого значения.
  • Добавьте целенаправленную привязку к представлениям меню.
  • Установите значение сцены в фокусе в структуре приложения или представлении более высокого уровня, например представлении содержимого.

Структура ключа с фокусированным значением должна соответствовать протоколу FocusedValueKey. Укажите псевдоним типа для выбранного типа значения. Следующий код создает сфокусированный тип значения для структуры документа SwiftUI:

struct DocumentFocusedValueKey: FocusedValueKey {
    typealias Value = Binding<Document>
}

Вычисляемое свойство расширения FocusedValues требует, чтобы вы добавили методы получения и установки для структуры ключа с фокусным значением, которую вы создаете.

extension FocusedValues {
    var document: DocumentFocusedValueKey.Value? {
        get {
            return self[DocumentFocusedValueKey.self]
        }
        
        set {
            self[DocumentFocusedValueKey.self] = newValue
        }
    }
}

Используйте оболочку свойства @FocusedBinding, чтобы добавить целенаправленную привязку к представлениям меню.

@FocusedBinding(\.document) var theDocument: Document?

Значение, которое вы указываете после @FocusedBinding, — это имя вычисляемого свойства, которое вы добавили в качестве расширения к FocusedValues.

Используйте модификатор .focusedSceneValue, чтобы установить значение сцены в фокусе. В следующем примере значение сцены в фокусе задается для документа в приложении на основе документов:

.focusedSceneValue(\.document, file.$document)

В этом примере вы должны установить модификатор .focusedSceneValue в структуре приложения.

Ответ принят как подходящий

У меня была эта проблема какое-то время, и я ничего не нашел по этому поводу. Но наконец я нашел эту страницу:

«.fileExporter и .fileImporter не будут работать внутри меню в SwiftUI» https://fpposchmann.de/fileexporter-and-fileimporter-wont-work-inside-a-menu-in-swiftui/ Спасибо Фрэнку-Питеру за идею.

Я переработал свой код, и теперь команды в CommandGroup работают как надо.

private struct FactorizedIExportButton: View {
    @Binding var isExporting: Bool

    var body: some View {
        Button(action: {
            isExporting = true
        }, label: {
            Label("Export…", systemImage: "square.and.arrow.up")
        })
        .keyboardShortcut("E")
    }
}

struct ImportButton: View {
    @State var isImporting: Bool = false

    var body: some View {
        FactorizedImportButton(isImporting: $isImporting)
            .fileImporter(
                isPresented: $isImporting,
                allowedContentTypes: [.text, .kmz],
                allowsMultipleSelection: true
            ) { result in importFiles(result) }
    }
}

struct ExportButton: View {
    @State var isExporting: Bool = false
    var document = TextDocument()

    var body: some View {
        FactorizedIExportButton(isExporting: $isExporting)
            .fileExporter(
                isPresented: $isExporting,
                document: document,
                contentTypes: [.gpx],
                defaultFilename: "Export"
            ) { result in exportFile(result) }
    }
}

struct ImportExportCommands: Commands {
    @State var isImporting: Bool = false
    @State var isExporting: Bool = false
    var document = TextDocument()

    @MainActor
    var body: some Commands {
        CommandGroup(replacing: .importExport) {
            Section {
                FactorizedImportButton(isImporting: $isImporting)
                FactorizedIExportButton(isExporting: $isExporting)
            }
            .fileImporter(
                isPresented: $isImporting,
                allowedContentTypes: [.text, .kmz],
                allowsMultipleSelection: true
            ) { result in importFiles(result) }
            .fileExporter(
                isPresented: $isExporting,
                document: document,
                contentTypes: [.gpx],
                defaultFilename: "Export"
            ) { result in exportFile(result) }
        }
    }
}

Другие вопросы по теме