URLImage
是一个 SwiftUI 视图,用于显示从提供的 URL 下载的图像。URLImage
会为您管理远程图像的下载和本地缓存(包括内存和磁盘)。
使用 URLImage
非常简单
URLImage(url: url) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
}
请在演示应用程序中查看一些示例。
可以使用 Swift Package Manager 安装 URLImage
。
在 Xcode 中,打开 File/Swift Packages/Add Package Dependency... 菜单。
复制并粘贴包 URL
https://github.com/dmytro-anokhin/url-image
有关更多详细信息,请参阅 Adding Package Dependencies to Your App 文档。
您可以使用 URL 和 ViewBuilder
创建 URLImage
以显示下载的图像。
import URLImage // Import the package module
let url: URL = //...
URLImage(url) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
}
注意:URLImage
初始化的第一个参数是 URL
类型,如果您有一个 String
,您必须首先创建一个 URL
对象。
URLImage
视图管理并在 4 个下载状态之间转换
每个状态都有一个单独的视图。 您可以使用 ViewBuilder
参数自定义一个或多个。
URLImage(item.imageURL) {
// This view is displayed before download starts
EmptyView()
} inProgress: { progress in
// Display progress
Text("Loading...")
} failure: { error, retry in
// Display error and retry button
VStack {
Text(error.localizedDescription)
Button("Retry", action: retry)
}
} content: { image in
// Downloaded image
image
.resizable()
.aspectRatio(contentMode: .fit)
}
URLImage
允许使用 URLImageOptions
结构控制某些方面。 例如,是否下载图像或使用缓存、何时启动和取消下载、如何配置网络请求、最大像素大小等。
URLImageOptions
是环境值,可以使用 \.urlImageOptions
键路径设置。
URLImage(url) { image in
image
.resizable()
.aspectRatio(contentMode: .fit)
}
.environment(\.urlImageOptions, URLImageOptions(
maxPixelSize: CGSize(width: 600.0, height: 600.0)
))
在环境值中设置 URLImageOptions
允许为您的整个或部分视图层次结构设置选项。
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.urlImageOptions, URLImageOptions(
maxPixelSize: CGSize(width: 600.0, height: 600.0)
))
}
}
}
如果需要有关图像的信息(例如实际大小)或访问底层 CGImage
对象,可以使用 ImageInfo
结构。 ImageInfo
是 content
视图构建器闭包的参数。
URLImage(item.imageURL) { image, info in
if info.size.width < 1024.0 {
image
.resizable()
.aspectRatio(contentMode: .fit)
} else {
image
.resizable()
.aspectRatio(contentMode: .fill)
}
}
如果想添加缩放图像的功能,请考虑查看 AdvancedScrollView 包。
import AdvancedScrollView
import URLImage
URLImage(url) { image in
AdvancedScrollView(magnificationRange: 1.0...4.0) { _ in
image
}
}
URLImage
还可以缓存图像以降低网络带宽或供离线使用。
默认情况下,URLImage
使用协议缓存策略,即 Cache-Control HTTP 标头和 URLCache
。 这与图像在 Web 上的工作方式相对应,并且需要网络连接。
或者,如果您想离线查看图像,则必须配置文件存储。 配置后,URLImage
将不再使用协议缓存策略,而是遵循 URLImageOptions.FetchPolicy
设置。
import URLImage
import URLImageStore
@main
struct MyApp: App {
var body: some Scene {
let urlImageService = URLImageService(fileStore: URLImageFileStore(),
inMemoryStore: URLImageInMemoryStore())
return WindowGroup {
FeedListView()
.environment(\.urlImageService, urlImageService)
}
}
}
确保在目标设置的“Frameworks, Libraries,and Embedded Content”下包含 URLImageStore
库。
您可能会问何时使用协议缓存或自定义缓存。 URLImage
旨在服务于两种使用场景
当应用程序只能连接到互联网时,使用协议缓存策略。 电子商务应用程序(如购物、旅游、活动预订应用程序等)就是这样工作的。 遵循协议缓存策略,您可以确保图像以 CDN 定义的方式缓存,仍然可以快速访问,并且不会占用用户设备上不必要的空间。
为需要在离线访问或在后台下载的内容配置 URLImageStore
。 这可以是一个阅读器应用程序,您可能希望在用户打开文章之前下载它们,也许在应用程序处于后台时。 此内容应保留相当长一段时间。
当图像视图呈现时,URLImage
开始加载。 在某些情况下(如使用 List
时),您可能希望在视图出现时开始加载,并在视图消失时取消加载。 您可以使用 URLImageOptions.LoadOptions
选项自定义此设置。 您可以组合多个选项以实现最适合您的 UI 的行为。
List(/* ... */) {
// ...
}
.environment(\.urlImageOptions, URLImageOptions(loadOptions: [ .loadOnAppear, .cancelOnDisappear ]))
注意:3.1 之前的版本在出现时开始加载,并在视图消失时取消加载。 3.1 版本在视图呈现时开始加载。 这是因为在没有上下文的情况下,onAppear
和 onDisappear
回调是相当不可预测的。
或者,您可以创建自己的 URLImage
来自定义外观和行为以满足您的需求。
struct MyURLImage: View {
@ObservedObject private var remoteImage: RemoteImage
init(service: URLImageService, url: URL) {
remoteImage = service.makeRemoteImage(url: url, identifier: nil, options: URLImageOptions())
}
var body: some View {
ZStack {
switch remoteImage.loadingState {
case .success(let value):
value.image
default:
EmptyView()
}
}
.onAppear {
remoteImage.load()
}
}
}
您可以从封闭视图访问服务环境值:@Environment(\.urlImageService) var service: URLImageService
。
您可能希望在没有视图的情况下下载图像。 这可以使用 RemoteImagePublisher
对象来实现。 RemoteImagePublisher
可以缓存图像以供 URLImage
视图将来使用。
将图像下载为 CGImage
并忽略任何错误
cancellable = URLImageService.shared.remoteImagePublisher(url)
.tryMap { $0.cgImage }
.catch { _ in
Just(nil)
}
.sink { image in
// image is CGImage or nil
}
将多个图像下载为 [CGImage?]
数组
let publishers = urls.map { URLImageService.shared.remoteImagePublisher($0) }
cancellable = Publishers.MergeMany(publishers)
.tryMap { $0.cgImage }
.catch { _ in
Just(nil)
}
.collect()
.sink { images in
// images is [CGImage?]
}
使用 RemoteImagePublisher
对象下载图像时,所有选项都像对 URLImage
对象一样适用。 默认情况下,下载的图像将缓存在磁盘上。 这可以加快在应用程序的后期阶段显示图像的速度。 此外,这是目前在 iOS 14 小部件中显示图像的唯一支持方式。
不幸的是,WidgetKit 中的视图无法运行异步操作:https://developer.apple.com/forums/thread/652581。 推荐的方法是在 TimelineProvider
中加载您的内容,包括图像。
您仍然可以使用 URLImage
来实现此目的。 想法是您使用 RemoteImagePublisher
对象在 TimelineProvider
中加载图像,并在 URLImage
视图中显示它。
URLImage
初始化程序现在省略了第一个参数的参数标签,使 URLImage(url: url)
变为 URLImage(url)
。URLImage
初始化程序现在为构建视图的闭包使用 ViewBuilder
属性。URLImageOptions
现在在环境中传递,而不是作为参数传递。 自定义标识符仍然可以作为 URLImage
的参数传递。URLImage
使用协议缓存策略和 URLCache
。 这不会存储图像以供离线使用。 您可以按照 cache 部分中的描述配置文件存储。如果您将 URLImage
与 TextField
或另一个更新触发视图更新的状态的控件一起使用,这是一个常见问题。 由于 URLImage
是异步的并且最初为空,它将在显示下载的图像之前重置为空状态。 为避免这种情况,请在您的 App
中的某个位置设置 URLImageInMemoryStore
。
import SwiftUI
import URLImage
import URLImageStore
@main
struct MyApp: App {
var body: some Scene {
let urlImageService = URLImageService(fileStore: nil, inMemoryStore: URLImageInMemoryStore())
return WindowGroup {
ContentView()
.environment(\.urlImageService, urlImageService)
}
}
}
注意:您可以使用 URLImageInMemoryStore
的 removeImageWithURL
、removeImageWithIdentifier
或 removeAllImages
方法重置缓存的图像。
这不是 Bug。 导航/工具栏使用 .renderingMode(.template)
将图像显示为模板(将所有非透明像素呈现为前景色)。 重置它的方法是指定 .renderingMode(.original)
URLImage(url) { image in
image.renderingMode(.original)
}
使用 GitHub Issues 报告 Bug。 尽可能包含以下信息
摘要和/或背景; 操作系统以及您使用的设备; URLImage 库的版本; 您期望会发生什么; 实际发生了什么; 附加信息:显示 Bug 的屏幕截图或视频; 崩溃日志; 示例代码,尝试隔离它,以便它可以在没有依赖项的情况下编译; 测试数据:如果您使用公共资源,请提供图像的 URL。
请确保存在可重现的场景。 理想情况下,提供示例代码。 如果您提交示例代码 - 确保它可以编译 ;)
使用 GitHub Issues 请求功能。
欢迎贡献。 请在提交 Pull Request 之前创建一个 GitHub Issue 来计划和讨论实现。