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
Overriding Methods in Swift Extensions
How to Make a Swiftui List Scroll Automatically
How to Pop to the Root View Using Swiftui
How Does String Substring Work in Swift
Saving Custom Swift Class With Nscoding to Userdefaults
How to Provide a Localized Description With an Error Type in Swift
How to Cast Self to Unsafemutablepointer≪Void≫ Type in Swift
What Is the Purpose of Willset and Didset in Swift
How to Convert a Date String With Optional Fractional Seconds Using Codable in Swift
How to Remove Diacritics from a String in Swift
Transparent Background For Modally Presented Viewcontroller
How Change Background Color If Using Navigationview in Swiftui
How to Use Assets Catalog Color Sets
How to Get Monday'S Date of the Current Week in Swift