Reputation: 1082
I'm trying to use the new WindowGroup to display a more complex view in a new window but somehow I can't figure out how to pass values into the view.
Until yet I was playing around with NSWindow but there I can't use the new toolbar with .toolbar{}
and I'm somehow getting weird errors when using the latest swiftUI features.
In my old code I just could pass my values into the new view like usual:
.simultaneousGesture(
TapGesture(count: 2)
.onEnded {
var window: NSWindow!
if nil == window {
// parse my struct content(name: "XServe-Test", configFile: "/FileUrl", permissions: .rootPermissions, cluster: cluster(x12, CPUMax: .cores(28), ramMax: .gb(1200)))
let serverView = serverView(content: content)
window = NSWindow(
contentRect: NSRect(x: 20, y: 20, width: 580, height: 400),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered,
defer: false
)
window.center()
window.setFrameAutosaveName("ServerMainView")
window.isReleasedWhenClosed = false
window.title = content.name
window.contentView = NSHostingView(rootView: serverView)
window.toolbar = NSToolbar()
window.toolbarStyle = .unifiedCompact
}
window.makeKeyAndOrderFront(nil)
}
)
Now I'm using in the app file:
import SwiftUI
@main
struct myApp: App {
var body: some Scene {
WindowGroup {
contentView()
}
.windowStyle(.hiddenTitleBar)
.commands {
SidebarCommands()
ToolbarCommands()
}
// the view that should be displayed in a new window
WindowGroup("serverView") {
let inputContent : content = content(name: "XServe-Test", configFile: "/FileUrl", permissions: .rootPermissions, cluster: cluster(x12, CPUMax: .cores(28), ramMax: .gb(1200)))
serverView(content: inputContent) // this is static now :(
}
.handlesExternalEvents(matching: Set(arrayLiteral: "serverView"))
}
and the following code to open the view:
.simultaneousGesture(
TapGesture(count: 2)
.onEnded {
guard let url = URL(string: "com-code-myApp://serverView") else { return }
NSWorkspace.shared.open(url)
}
)
How do I pass the input from the tap gesture into the new view using the WindowGroup logic?
Upvotes: 4
Views: 3201
Reputation: 65
I found this example at Apple Developer
First, You need Codable
& Hashable
struct for your data. For example, If I would like to pass an url when opening a view:
struct File: Codable, Hashable {
var id = UUID()
let path: URL
}
Now we can pass this struct between windows.
@main
struct myApp: App {
var body: some Scene {
// Default View
WindowGroup {
ContentView()
}
// Open FileView() with given data.
WindowGroup(id: "file.view", for: File.self) { $file in
FileView(file: $file)
} default: { // fallback
File(path: URL(string: ""))
}
}
}
FileView should look like below:
struct FileView: View {
@Binding var file: File
var body: some View{
Text(file.path.absoluteString)
}
}
You can open FileView() everywhere. This is an example to open FileView() with data in ContentView()
struct ContentView: View {
@Environment(\.openWindow) private var openWindow
@Environment(\.dismissWindow) private var dismissWindow
var body: some View{
Button("hit me") {
openWindow(id: "file.view", value: File(path: URL(string: "file://")!)
}
}
}
Upvotes: 4
Reputation: 1082
I found two ways to solve this problem. I'm using the first one, because my application is file based. The second solution is based on the great Pulse git repo.
In both cases you need to register a custom URL in the Xcode project settings under:
Tragets -> yourApp -> Info -> URL Types, otherwise it won't work.
first solution:
import SwiftUI
@main
struct myApp: App {
var body: some Scene {
// 'default' view
WindowGroup { contentView() }
// the view that should open if someone opens your file
WindowGroup("fileView") { fileView() }
.handlesExternalEvents(matching: ["file"]) // does the magic
}
}
struct fileView: View {
var body: some View {
VStack{ /* your view content */}
.onOpenURL(perform: { url in
// get url and read e.g.: your info file
})
}
}
// you can open a file using:
Button("openMyFileinAnExtraWindow"){
let fileUrl = URL(fileURLWithPath: "~/Documents/myFile.yourExtension")
NSWorkspace.shared.open(fileUrl)
}
second solution:
Notice: I created this code snippet based on the great Pulse git repo.
import SwiftUI
@main
struct myApp: App {
var body: some Scene {
// 'default' view
WindowGroup { contentView() }
// the view that should open if someone opens your file
WindowGroup { DetailsView() }
.handlesExternalEvents(matching: Set(arrayLiteral: "newWindow")) // this url must be registerd
}
}
struct DetailsView: View {
var body: some View {
ExternalEvents.open
}
}
public struct ExternalEvents {
/// - warning: Don't use it, it's used internally.
public static var open: AnyView?
}
struct contentView: View {
var body: some View {
VStack {
// create a button that opens a new window
Button("open a new window") {
ExternalEvents.open = AnyView(
newWindow(id: 0, WindowName: "I am a new window!" )
)
guard let url = URL(string: "your-url://newWindow") else { return }
NSWorkspace.shared.open(url)
}
}
}
}
struct newWindow: View {
var id: Int
var WindowName: String
var body: some View{
VStack{
Text(WindowName + String(id))
}
}
}
I'm not sure if this is the best way to pass variables to a new window, but it does the job quite convincingly. I'm happy about any solution approaches and ideas.
Upvotes: 2