在 Mac Catalyst 中打开一个新窗口

Posted

技术标签:

【中文标题】在 Mac Catalyst 中打开一个新窗口【英文标题】:Open a new window in Mac Catalyst 【发布时间】:2020-03-11 21:40:48 【问题描述】:

我正在使用 Mac Catalyst 移植一个 iPad 应用程序。我正在尝试在新窗口中打开视图控制器。

如果我严格使用 AppKit,我可以按照post 中的描述做一些事情。但是,由于我使用的是 UIKit,所以没有可用的 showWindow() 方法。

This article 声明这可以通过在项目的新包中添加 AppKit 来实现(我这样做了),但是它没有解释如何实际呈现新窗口的细节。上面写着……

另一件你不能完全做的事情是产生一个新的NSWindow 和一个 UIKit 视图层次结构。 然而,您的 UIKit 代码能够生成一个新的窗口场景,而您的 AppKit 代码能够获取它所呈现的结果 NSWindow 并劫持它来做任何事情你想要它,所以从这个意义上说,你可以为辅助调色板和各种其他功能生成 UIKit 窗口。

有人知道如何实现本文中解释的内容吗?

TL;DR:我如何使用 Mac Catalyst 打开 UIViewController 作为新的单独 NSWindow

【问题讨论】:

哇,我不得不为此深入研究一个兔子洞,但我想我已经完成了我的回答。我在我的项目中的系统上运行它,所以请随时提出问题。 【参考方案1】:

使用 SwiftUI,您可以这样做(感谢 Ron Sebro):

1。激活多窗口支持:

2。请求新场景:

struct ContentView: View 
    var body: some View 
        VStack 
            // Open window type 1
            Button(action: 
                UIApplication.shared.requestSceneSessionActivation(nil,
                                                                   userActivity: NSUserActivity(activityType: "window1"),
                                                                   options: nil,
                                                                   errorHandler: nil)
            ) 
                Text("Open new window - Type 1")
            

            // Open window type 2
            Button(action: 
                UIApplication.shared.requestSceneSessionActivation(nil,
                                                                   userActivity: NSUserActivity(activityType: "window2"),
                                                                   options: nil,
                                                                   errorHandler: nil)
            ) 
                Text("Open new window - Type 2")
            
        
    

3。创建新的窗口视图:

struct Window1: View 
    var body: some View 
        Text("Window1")
    

struct Window2: View 
    var body: some View 
        Text("Window2")
    

4。更改 SceneDelegate.swift:

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 
        if let windowScene = scene as? UIWindowScene 
            let window = UIWindow(windowScene: windowScene)

            if connectionOptions.userActivities.first?.activityType == "window1" 
                window.rootViewController = UIHostingController(rootView: Window1())
             else if connectionOptions.userActivities.first?.activityType == "window2" 
                window.rootViewController = UIHostingController(rootView: Window2())
             else 
                window.rootViewController = UIHostingController(rootView: ContentView())
            

            self.window = window
            window.makeKeyAndVisible()
        
    

【讨论】:

【参考方案2】:

编辑:添加了有关如何拥有其他不同窗口(如面板)的信息

为了在mac上支持多窗口,你需要做的就是在iPad上支持多窗口。

你可以在thisWWDC session 22:28 开始找到你需要的所有信息,但总结起来你需要做的是支持新的场景生命周期模型。

首先编辑您的目标并检查支持多窗口复选标记

完成此操作后,单击配置选项,该选项应将您带到 info.plist。 确保您有正确的应用场景清单条目

创建一个名为 SceneDelegate.swift 的新 swift 文件,然后将以下样板代码粘贴到其中

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate 

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) 
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).

        // Create the SwiftUI view that provides the window contents.
       guard let _ = (scene as? UIWindowScene) else  return 
    

    func sceneDidDisconnect(_ scene: UIScene) 
        // Called as the scene is being released by the system.
        // This occurs shortly after the scene enters the background, or when its session is discarded.
        // Release any resources associated with this scene that can be re-created the next time the scene connects.
        // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
    

    func sceneDidBecomeActive(_ scene: UIScene) 
        // Called when the scene has moved from an inactive state to an active state.
        // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
    

    func sceneWillResignActive(_ scene: UIScene) 
        // Called when the scene will move from an active state to an inactive state.
        // This may occur due to temporary interruptions (ex. an incoming phone call).
    

    func sceneWillEnterForeground(_ scene: UIScene) 
        // Called as the scene transitions from the background to the foreground.
        // Use this method to undo the changes made on entering the background.
    

    func sceneDidEnterBackground(_ scene: UIScene) 
        // Called as the scene transitions from the foreground to the background.
        // Use this method to save data, release shared resources, and store enough scene-specific state information
        // to restore the scene back to its current state.
    



你基本上完成了。运行您的应用程序,然后按 command + N 创建任意数量的新窗口。

如果你想在代码中创建一个新窗口,你可以使用这个:

@IBAction func newWindow(_ sender: Any)             
    UIApplication.shared.requestSceneSessionActivation(nil, userActivity: nil, options: nil)  (error) in
        //
    

现在我们来了解如何创建额外窗口的大奥秘

关键是在应用程序中创建多种场景类型。您可以在我无法正常工作的 info.plist 或 AppDelegate 中执行此操作。

让我们将创建新窗口的函数更改为:

@IBAction func newWindow(_ sender: Any)      
    var activity = NSUserActivity(activityType: "panel")
    UIApplication.shared.requestSceneSessionActivation(nil, userActivity: activity, options: nil)  (error) in

    

为您的新场景创建一个新的故事板,创建至少一个视图控制器并确保设置为故事板中的初始视图控制器。

让我们在 appdelegate 中添加以下函数:

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration  
        if options.userActivities.first?.activityType == "panel" 
            let configuration = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
            configuration.delegateClass = CustomSceneDelegate.self
            configuration.storyboard = UIStoryboard(name: "CustomScene", bundle: Bundle.main)
            return configuration
         else 
            let configuration = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
            configuration.delegateClass = SceneDelegate.self
            configuration.storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
            return configuration
        
    

通过在请求场景时设置 userActivity,我们可以知道要创建哪个场景并相应地为其创建配置。菜单中的新窗口或 CMD+N 仍将创建您的默认新窗口,但新窗口按钮现在将从您的新故事板创建 UI。

还有多田:

【讨论】:

@Sam :我为我的项目做了同样的事情,但是在添加了多窗口支持之后,我在启动时的窗口大小行为与之前的行为不同。早些时候它会记住用户调整窗口大小的大小;在随后的发射中。它不再是相同的行为,窗口仅以特定大小打开。您是否也看到了相同的行为?

以上是关于在 Mac Catalyst 中打开一个新窗口的主要内容,如果未能解决你的问题,请参考以下文章

如何更改 Mac Catalyst 窗口场景中的窗口标题?

如何在 Mac Catalyst 中检测窗口大小调整?

Mac-catalyst - Mac 催化剂应用程序的最小窗口大小

Mac Catalyst 调整屏幕截图的窗口大小

如何在 Mac Catalyst 13.0+ swift 中打开 Finder

是否可以在 Mac Catalyst 中使用全屏?