-1

这个SwiftUI-Kit是一个开源项目,作为展示所有 SwiftUI 组件的一种方式,它支持所有 Apple 平台。

该项目是在 Xcode 12 测试版中创建的,使用新的 SwiftUIApp协议来处理应用程序的生命周期,部署目标是 iOS 14。

现在,我想在项目中添加对 iOS 13 的支持。而且我找不到在这个项目中同时拥有App适用于 iOS 14 和其他平台的协议并AppDelegate用于 iOS 13 的方法。

我尝试了 App Delegate 和 Scene Delegate 方法的不同组合。最终结果是 iOS 13 设备崩溃并出现以下错误。

dyld: Symbol not found: _$s7SwiftUI4ViewPAAE18navigationBarTitle_11displayModeQrqd___AA010NavigationE4ItemV0f7DisplayH0OtSyRd__lFQOMQ
  Referenced from: /private/var/containers/Bundle/Application/0813D699-9718-4106-BBC6/SwiftUI Kit iOS.app/SwiftUI Kit iOS
  Expected in: /System/Library/Frameworks/SwiftUI.framework/SwiftUI
 in /private/var/containers/Bundle/Application/0813D699-9718-4106-BBC6/SwiftUI Kit iOS.app/SwiftUI Kit iOS
dyld: launch, loading dependent libraries
DYLD_LIBRARY_PATH=/usr/lib/system/introspection
DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib

这是代码。您可以在此分支中找到 iOS 13 的完整项目代码。

import UIKit
import SwiftUI

#if os(iOS)

class AppDelegate: UIResponder, UIApplicationDelegate {...}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // ...
        let contentView = ContentView()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }

    func sceneDidDisconnect(_ scene: UIScene) {...}

    func sceneDidBecomeActive(_ scene: UIScene) {...}

    func sceneWillResignActive(_ scene: UIScene) {...}

    func sceneWillEnterForeground(_ scene: UIScene) {...}

    func sceneDidEnterBackground(_ scene: UIScene) {...}
}

@main
struct MainApp {
    static func main() {
        if #available(iOS 14.0, *) {
            SwiftUI_Kit_iOS_App.main()
        } else {
            UIApplicationMain(
                CommandLine.argc,
                CommandLine.unsafeArgv,
                nil,
                NSStringFromClass(AppDelegate.self)
            )
        }
    }
}

@available(iOS 14.0, *)
struct SwiftUI_Kit_iOS_App: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

#else

@main
struct SwiftUI_KitApp: App {
    var body: some Scene {
        WindowGroup {
            #if os(macOS)
            ContentView().frame(minWidth: 100, idealWidth: 300, maxWidth: .infinity, minHeight: 100, idealHeight: 200, maxHeight: .infinity)
            #else
            ContentView()
            #endif
        }
    }
}

#endif

我查了这个问题,但答案需要 iOS 14 作为目标。我想让它以 iOS 13 为目标。

4

1 回答 1

1

好的,我终于找到了解决方案!

不,它不漂亮,不,它不好看,但它非常正确,并且据我所知它有效。所以,这里是:

import SwiftUI

@main
struct MainApp {
    static func main() {
        if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) {
            SwiftUIApp.main()
        } else {
            #if os(iOS) // iOS 13.0 or lower
            UIApplicationMain(CommandLine.argc,
                              CommandLine.unsafeArgv,
                              nil,
                              NSStringFromClass(AppDelegate.self))
            #else
            // So we are on macOS 10.15, tvOS 13.0, watchOS 6.0 or someting lower.
            // By correctly setting the deployment target in your project,
            // you won't need to do someting here, as this situation will
            // never occur.
            print("This app doesn't run (yet) on this OS, so Bye")
            return
            #endif
        }
    }
}

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
struct SwiftUIApp: App {
    var body: some Scene {
        return WindowGroup {
            #if os(macOS)
            ContentView().frame(minWidth: 100, idealWidth: 300, maxWidth: .infinity, minHeight: 100, idealHeight: 200, maxHeight: .infinity)
            #else
            ContentView()
            #endif
        }
    }
}

struct ContentView: View {
    var body: some View {
        Text("Hello world!")
    }
}

#if os(iOS)
import UIKit
// @UIApplicationMain <- remove that!
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.

        return true
    }
    
    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }

}

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

        let contentView = ContentView()

        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }

    func sceneDidDisconnect(_ scene: UIScene) { /*...*/ }
    func sceneDidBecomeActive(_ scene: UIScene) { /*...*/ }
    func sceneWillResignActive(_ scene: UIScene) { /*...*/ }
    func sceneWillEnterForeground(_ scene: UIScene) { /*...*/ }
    func sceneDidEnterBackground(_ scene: UIScene) { /*...*/ }
}
#endif

代码很多吧?

但是,(就我而言)这还不是全部。我们还需要深入研究 iOS 目标的 Info.plist 文件。

  1. 找到名为Application Scene Manifestor的键UIApplicationSceneManifest并将其展开(通过单击灰色三角形)

  2. 添加以下内容,使其如下图所示: 信息列表

    确保在“默认配置”中填写的内容与这行代码中的内容完全相同:

            return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    

    此外,“SceneDelegate”应该是SceneDelegate类的名称,“LaunchScreen”应该是启动屏幕故事板的名称(有时它写有空格,有时没有,所以要小心!)。

    如果您使用我提供的代码,并且不重命名这些内容之一,这可能不会成为问题。

  3. 最后,从设备中删除该应用程序。这将确保在重新运行和安装时复制新的 Info.plist。(仅在 Info.plist 中进行更改时需要)

于 2020-07-14T21:25:33.140 回答