Как создать приложение SwiftUI DocumentGroup, не запуская средство выбора файлов?
Если пользователь использует шаблон приложения документа в Xcode для создания приложения SwiftUI, macOS запускает его с нового документа. Это хорошо. Я могу работать с этим, чтобы представить интерфейс пользователя в новом документе.
Однако с тем же приложением, работающим на iOS, пользователь вместо этого приветствует стандартного контроллера представления документа для создания или выбора документа.
Это бесполезно, потому что у меня нет возможности предоставить информацию о подключении или другую настраиваемую информацию.
Я заметил, что если вы добавите WindowGroup к Scene, приложение отобразит эту группу окон. Но тогда я не знаю, как вызвать пользователя в пользовательский интерфейс средства выбора.
Кто-нибудь придумал, как делать такие вещи, как презентация на борту поверх этого приложения на основе DocumentGroup?
Вот полное приложение для документов
import SwiftUI
import UniformTypeIdentifiers
@main
struct DocumentTestApp: App {
var body: some Scene {
DocumentGroup(newDocument: DocumentTestDocument()) { file in
ContentView(document: file.$document)
}
}
}
struct ContentView: View {
@Binding var document: DocumentTestDocument
var body: some View {
TextEditor(text: $document.text)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(document: .constant(DocumentTestDocument()))
}
}
extension UTType {
static var exampleText: UTType {
UTType(importedAs: "com.example.plain-text")
}
}
struct DocumentTestDocument: FileDocument {
var text: String
init(text: String = "Hello, world!") {
self.text = text
}
static var readableContentTypes: [UTType] { [.exampleText] }
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let string = String(data: data, encoding: .utf8)
else {
throw CocoaError(.fileReadCorruptFile)
}
text = string
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
let data = text.data(using: .utf8)!
return .init(regularFileWithContents: data)
}
}
3 ответа
Хорошо, друзья, вот хороший и хитрый способ заставить все работать, добраться до ключевых окон и настроить onboarding/paywall/все, что вы хотите!
import SwiftUI
@main
struct ExampleApp: App {
@StateObject var captain = Captain()
var body: some Scene {
DocumentGroup(newDocument: ExampleOfDocumentGroupAndOnboardingPaywallDocument()) { file in
ContentView(document: file.$document)
}
}
}
class Captain: ObservableObject {
var onboardingSheet: UIViewController?
@objc func loadData() {
onboardingSheet = try? OnboardingOrPaywall(dismissHandler: dismissSheet).presentFromDocumentGroup()
}
func dismissSheet() {
onboardingSheet?.dismiss(animated: true)
}
init() {
NotificationCenter.default.addObserver(self,
selector: #selector(loadData),
name: UIApplication.didBecomeActiveNotification,
object: nil)
}
}
public protocol DocumentGroupSheet: View {}
struct OnboardingOrPaywall: DocumentGroupSheet {
var dismissHandler: () -> Void
var body: some View {
Button("Done") {
dismissHandler()
}
Text("Let me introduce you to this delicious app!")
}
}
public enum DocumentGroupSheetError: Error {
case noParentWindow
}
public extension DocumentGroupSheet {
func presentFromDocumentGroup() throws -> UIViewController {
let window = UIApplication.shared.activeKeyWindows.first
let parent = window?.rootViewController
guard let parent = parent else { throw DocumentGroupSheetError.noParentWindow }
let sheet = UIHostingController(rootView: body)
sheet.modalPresentationStyle = .fullScreen
parent.present(sheet, animated: false, completion: nil)
return sheet
}
}
public extension UIApplication {
var activeWindowScenes: [UIWindowScene] {
connectedScenes
.filter { $0.activationState == .foregroundActive }
.compactMap { $0 as? UIWindowScene }
}
var activeWindows: [UIWindow] {
activeWindowScenes
.flatMap { $0.windows }
}
var activeKeyWindows: [UIWindow] {
activeWindows
.filter { $0.isKeyWindow }
}
}
Похоже, это теперь возможно начиная с iOS 16 с документацией здесь .
@main
struct Mail: App {
var body: some Scene {
WindowGroup(id: "mail-viewer") {
MailViewer()
}
}
}
struct NewViewerButton: View {
@Environment(\.openWindow) private var openWindow
var body: some View {
Button("Open new mail viewer") {
openWindow(id: "mail-viewer")
}
}
}
Итак, когда появится сцена, вы можете открыть окно регистрации.
Приложение по умолчанию показывает первую сцену окна, поэтому поместите сначала сцену окна, а затем DocumentGroup. Где-то в конце процесса посадки (успешный путь) вызовите контроллер документов для создания нового документа (DocumentGroup основан на том же механизме NSDocumentController внутри).
Обновление: ниже для macOS
* только что узнал, что исходный вопрос для iOS
Таким образом, возможный подход
@main
struct DocumentTestApp: App {
var body: some Scene {
WindowGroup("On-Boarding") {
// ContentView()
// In some action at the end of this scene flow
// just close current window and open new document
Button("Demo") {
NSApp.sendAction(#selector(NSWindow.performClose(_:)), to: nil, from: nil)
NSDocumentController.shared.newDocument(nil)
}
}
DocumentGroup(newDocument: DocumentTestDocument()) { file in
ContentView(document: file.$document)
}
}
}