ODRManager 可以轻松地为任何 SwiftUI 应用程序添加按需资源 (On Demand Resource) 支持,并且支持在 SwiftUI 中使用标准化的内容加载覆盖层 (Content Loading Overlay)。
如果您觉得 ODRManager 有用,并希望帮助支持其持续开发和维护,请考虑进行小额捐赠,尤其是在您将其用于商业产品时。
正是通过像您这样的贡献者的支持,我才能继续免费构建、发布和维护高质量、文档完善的 Swift 包,例如 ODRManager。
Swift Package Manager (Xcode 11 及以上版本)
https://github.com/Appracatappra/ODRManager.git 粘贴到对话框中。为什么不使用 CocoaPods、Carthage 或其他方式?
支持多个依赖项管理器会使维护库的复杂性和时间成本呈指数级增长。
由于 Swift Package Manager 已与 Xcode 11(及更高版本)集成,因此它是未来支持的最简单选择。
通过在应用程序中包含 ODRManager 并在源代码中标记特定内容,您可以轻松地为您的 SwiftUI 应用程序添加按需资源支持。 ODRManager 包还包含一个标准化的内容加载覆盖层,您可以在应用程序等待 ODR 内容加载时显示它。
此包专门设计用于游戏应用程序,因此需要
SwiftUIGamepad包来支持内置ODRContentLoadingOverlay视图中的游戏手柄交互。如果您想在没有这些额外要求的情况下使用
ODRManager,只需将ODRManager、ODRRequest和OnDemandResources直接复制到应用程序的项目中即可。
在您的 Swift 应用程序中使用游戏手柄之前,您需要启用支持。 在 Xcode 中,选择您的应用程序的 Project > Signing & Capabilities > + Capability 并添加 Game Controllers。
启用后,从复选框列表中选择您想要支持的游戏手柄类型。
如果您启用了 Micro Gamepad,它可能会阻止 Apple TV 识别已连接的 Extended Gamepad。 如果您在 tvOS 应用程序中使用此包,我建议禁用它。
当您将想要稍后使用 ODRManager 下载的内容包含到您的应用程序中时,您将使用 Xcode 的 On Demand Resource Tag 属性将 ODR Tag 分配给该内容。 例如,您可以标记 Asset Catalog 中的项目。
所有具有相同 On Demand Resource Tag 的项目将被收集在一起并构建到 ODR Package 中,应用程序稍后可以使用 ODCManager 下载该包。 在 Xcode 的应用程序的 Project > Resource Tags 下,您可以看到所有 ODR Packages 及其构建大小。
有关使用 On Demand Resources 的更多信息,请参阅 Apple 的文档: https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/On_Demand_Resources_Guide/index.html#//apple_ref/doc/uid/TP40015083-CH2-SW1
在您的应用程序中发出对按需资源的请求之前,您需要配置 OnDemandResources.onRequestResourceFromBundle 以处理来自您的应用程序捆绑包的请求。
// Make the `NSBundleResourceRequest` against the App Bundle and not the Package.
OnDemandResources.onRequestResourceFromBundle = {tag in
return NSBundleResourceRequest(tags: [tag])
}
请参阅下面的 在哪里设置样式更改 > AppDelegate > willFinishLaunchingWithOptions 以查看定义
OnDemandResources.onRequestResourceFromBundle闭包的首选区域。
ODRManager 可以轻松请求 按需资源 内容并对内容加载或加载失败做出反应。 此外,ODRManager 可以轻松地在后台预先请求内容,以便在需要时最终用户在使用您的应用程序时不会中断。 例如
ODRManager.shared.prefetchResourceWith(tag: "Tag01,Tag02,...")
如果您需要请求特定内容,请使用 requestResourceWith 函数。 请参见示例
OnDemandResources.loadResourceTag = "Tag01"
ODRManager.shared.requestResourceWith(tag: OnDemandResources.loadResourceTag, onLoadingResource: {
Debug.info(subsystem: "MasterDataStore", category: "On Demand Resource", "Loading: \(OnDemandResources.loadResourceTag)")
OnDemandResources.lastResourceLoadError = ""
OnDemandResources.isLoadingResouces = true
}, onSuccess: {
Debug.info(subsystem: "MasterDataStore", category: "On Demand Resource", "Content Loaded: \(OnDemandResources.loadResourceTagg)")
OnDemandResources.lastResourceLoadError = ""
OnDemandResources.isLoadingResouces = false
// Handle load completing ...
}, onFailure: {error in
Log.error(subsystem: "MasterDataStore", category: "On Demand Resource", "Error: \(OnDemandResources.loadResourceTag) = \(error)")
OnDemandResources.lastResourceLoadError = error
// NOTE: Marking `isLoadingResouces` `true` so that the error can be displayed using a `ODRContentLoadingOverlay` in our UI
OnDemandResources.isLoadingResouces = true
})
ODRContentLoadingOverlay 视图可以用作应用程序 UI 中的标准内容加载和加载错误覆盖层。 例如
if OnDemandResources.isLoadingResouces {
ODRContentLoadingOverlay(onLoadedSuccessfully: {
// Handle the load completing ...
OnDemandResources.isLoadingResouces = false
}, onCancelDownload: {
// Handle the user wanting to cancel the download ...
OnDemandResources.isLoadingResouces = false
})
}
为了节省内存,您应该在使用完按需资源后释放它们。 例如
// Release any required resources
ODRManager.shared.releaseResourceWith(tag: "Tag01")
此外,您需要释放任何失败的下载尝试,以便可以再次尝试。 例如
// Release any failed resource load attempts so that they can be tried again.
ODRManager.shared.releaseFailedResourceLoads()
为了使样式更改生效,您需要在绘制任何 Views 之前进行更改。 您可以在您的主应用程序上使用以下代码
import SwiftUI
import SwiftletUtilities
import LogManager
import SwiftUIKit
import SwiftUIGamepad
import ODRManager
@main
struct PackageTesterApp: App {
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
@Environment(\.scenePhase) private var scenePhase
@Environment(\.colorScheme) var colorScheme
var body: some Scene {
WindowGroup {
ContentView()
}
.onChange(of: scenePhase) { oldScenePhase, newScenePhase in
switch newScenePhase {
case .active:
Debug.info(subsystem: "PackageTesterApp", category: "Scene Phase", "App is active")
case .inactive:
Debug.info(subsystem: "PackageTesterApp", category: "Scene Phase", "App is inactive")
case .background:
Debug.info(subsystem: "PackageTesterApp", category: "Scene Phase", "App is in background")
@unknown default:
Debug.notice(subsystem: "PackageTesterApp", category: "Scene Phase", "App has entered an unexpected scene: \(oldScenePhase), \(newScenePhase)")
}
}
}
}
/// Class the handle the event that would typically be handled by the Application Delegate so they can be handled in SwiftUI.
class AppDelegate: NSObject, UIApplicationDelegate {
/// Handles the app finishing launching
/// - Parameter application: The app that has started.
func applicationDidFinishLaunching(_ application: UIApplication) {
// Register to receive remote notifications
UIApplication.shared.registerForRemoteNotifications()
}
/// Handle the application getting ready to launch
/// - Parameters:
/// - application: The application that is going to launch.
/// - launchOptions: Any options being passed to the application at launch time.
/// - Returns: Returns `True` if the application can launch.
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
// Set any `ODRManager` global style defaults here before any `Views` are drawn.
// Set style defaults
OnDemandResources.fontColor = .white
// Make the `NSBundleResourceRequest` against the App Bundle and not the Package.
OnDemandResources.onRequestResourceFromBundle = {tag in
return NSBundleResourceRequest(tags: [tag])
}
return true
}
/// Handles the app receiving a remote notification
/// - Parameters:
/// - application: The app receiving the notifications.
/// - userInfo: The info that has been sent to the App.
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
}
}
使用此代码,在 func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool 中进行任何样式更改,它们将应用于之后构建的所有视图。
包 包含其所有功能的完整 DocC 文档。