Swiftui 2: the Way to Open View in New Window

Launch new window on iOS app using SwiftUI Lifecycle

After a very long research session, I finally found the property that does this in this blog post.

Turns out that requestSceneSessionActivation is necessary, but there's no need to use a SceneDelegate.

When creating the new session, set the targetContentIdentifier on the NSUserActivity object.

let activity = NSUserActivity(activityType: "newWindow")
activity.userInfo = ["some key":"some value"]
activity.targetContentIdentifier = "newWindow" // IMPORTANT
UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil)

Finally, make sure the new WindowGroup can handle the event with the identifier:

WindowGroup(id: "newWindow") {
Text("New Window!").padding()
}
.handlesExternalEvents(matching: ["newWindow"])

This solution retains the SwiftUI Lifecycle, and also works on Catalyst.


Update for iPadOS 16/macOS Ventura:

You can now use the new openWindow environment property:

@Environment(\.openWindow) private var openWindow
...
openWindow(id: "newWindow")

Make sure the id passed to openWindow is the same as the id set in the WindowGroup or Window initializer.

SwiftUI on MacOS. - Opening a new Window

Make your function generic and add view constraint.

static func newWindow<Content: View>(forSpecialView view: Content, title: String = "new Window") { // <-- Here

Another good and easy solution is to use View extension.

extension View {
private func newWindowInternal(with title: String) -> NSWindow {
let window = NSWindow(
contentRect: NSRect(x: 20, y: 20, width: 680, height: 600),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered,
defer: false)
window.center()
window.isReleasedWhenClosed = false
window.title = title
window.makeKeyAndOrderFront(nil)
return window
}

func openNewWindow(with title: String = "new Window") {
self.newWindowInternal(with: title).contentView = NSHostingView(rootView: self)
}
}

Usage:

struct ContentView: View {
var body: some View {
Button(action: {
ContentViewNewWindow().openNewWindow()
}) {
Text("Open New Window")
}
}
}

Button to open view in new window SwiftUI 5.3 for Mac OS X

You need to keep reference to created preferences window (like to the main window).

Here is possible solution (tested with Xcode 11.4 / macOS 10.15.5)

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {

var window: NSWindow!
var preferencesWindow: NSWindow! // << here

@objc func openPreferencesWindow() {
if nil == preferencesWindow { // create once !!
let preferencesView = PreferencesView()
// Create the preferences window and set content
preferencesWindow = NSWindow(
contentRect: NSRect(x: 20, y: 20, width: 480, height: 300),
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered,
defer: false)
preferencesWindow.center()
preferencesWindow.setFrameAutosaveName("Preferences")
preferencesWindow.isReleasedWhenClosed = false
preferencesWindow.contentView = NSHostingView(rootView: preferencesView)
}
preferencesWindow.makeKeyAndOrderFront(nil)
}

// ... other code

and now button would look like

Button(action: {
NSApp.sendAction(#selector(AppDelegate.openPreferencesWindow), to: nil, from:nil)
}) {
Text("Preferences").font(.largeTitle).foregroundColor(.primary)
}

backup

Context environment to new window in SwiftUI

HEUREKA!!

class WindowController<RootView: View>: NSWindowController {
convenience init(rootView: RootView, width: Int, height: Int) {
let persistenceController = PersistenceController.shared
let hostingController = NSHostingController(rootView: rootView.frame(width: CGFloat(width), height: CGFloat(height)).environment(\.managedObjectContext, persistenceController.container.viewContext))
let window = NSWindow(contentViewController: hostingController)
window.setContentSize(NSSize(width: width, height: height))
self.init(window: window)
}
}

That's the right code for the WindowController. The environment-bla has to be added after frame as parameter. The persistenceController into the init and here we go!

SwiftUI - New Page

you can do this simply with switch or if/else :)

class Wnd : View {
@State var firstShown: Bool

var body: some View {
if firstShown {
FirstView(firstShown: $firstShown)
}
else
{
SecondView(firstShown: $firstShown)
}
}
}

firstView and secondView have Binding to firstShown var


Another one solution is to use handlesExternalEvents for switching views.

sample of usage: https://stackoverflow.com/a/65415593/4423545


Another solution is to hide NavigationBar:

.navigationBarHidden(true)


Related Topics



Leave a reply



Submit