Har*_*lue 6 dependencies dependency-injection image swiftui
我已经使用了AsyncImage一段时间在 SwiftUI 应用程序中渲染远程图像。
https://developer.apple.com/documentation/swiftui/asyncimage
我现在需要渲染受保护资产的图像 - 我必须在标头中提供不记名令牌才能下载这些图像。
很明显,这AsyncImage不适合这里的工作,因为无论如何都无法自定义请求。
我有一个ImageLoader协议;
protocol ImageLoader {
func loadImage(for url: URL) async throws -> UIImage
}
Run Code Online (Sandbox Code Playgroud)
其中描述了我编写的用于下载远程图像的服务。
如何向视图层次结构中嵌套多个级别的视图提供此服务。
例如;
struct UserAvatarView: View {
private let name: String
private let initials: String
private let image: UIImage?
init(name: String, initials: String, image: UIImage?) {
self.name = name
self.initials = initials
self.image = image
}
var body: some View {
GeometryReader { proxy in
ZStack {
if let image = image {
Image(uiImage: image)
.resizable()
.scaledToFill()
.clipped()
.aspectRatio(contentMode: .fill)
.clipShape(Rectangle())
.frame(width: proxy.size.width - 4, height: proxy.size.width - 4)
.clipShape(Circle())
.padding(2)
} else {
Circle()
.fill(Color(AvatarColor.makeColor(forText: name)))
.padding(2)
Text(initials)
.font(Font(UIFont.preferredFont(for: .body, weight: .regular, size: proxy.size.width / 2.55)))
.foregroundColor(.white)
}
Circle()
.strokeBorder(Color.white, lineWidth: 2)
}
}
.shadowed()
}
}
Run Code Online (Sandbox Code Playgroud)
这个视图可以嵌套很多层,我已经把它写得尽可能愚蠢,但在某些时候我需要获取图像并更新这个视图。
这个视图本身应该获取图像吗?
我可以实现一个UserAvatarViewModel接受ImageLoader实例的模型 - 但我不清楚如何创建具有所需依赖项的模型。
我热衷于避免单例实例,并且我不认为视图应该负责创建视图模型,这排除了Environment向下传递加载程序的可能性。
我曾认为最上面的视图负责所有繁重的工作,但随后我可以将属性传递给视图,只是让它们将它们传递给它们的子视图,这又像是一种反模式。
小智 7
您只需要将 AsyncImage 组件替换为您自己的组件即可。我创建了这个组件,类似于 AsyncImage 但它接收一个自定义异步函数来获取图像
struct CustomAsyncImage<Content: View, Placeholder: View>: View {
@State var uiImage: UIImage?
let getImage: () async -> UIImage?
let content: (Image) -> Content
let placeholder: () -> Placeholder
init(
getImage: @escaping () async -> UIImage?,
@ViewBuilder content: @escaping (Image) -> Content,
@ViewBuilder placeholder: @escaping () -> Placeholder
){
self.getImage = getImage
self.content = content
self.placeholder = placeholder
}
var body: some View {
if let uiImage = uiImage {
content(Image(uiImage: uiImage))
}else {
placeholder()
.task {
self.uiImage = await getImage()
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个使用示例。
CustomAsyncImage(getImage: getImage) { image in
image
.resizable()
.scaledToFit()
.frame(maxWidth: .infinity)
} placeholder: {
Text("PlaceHolder")
}
Run Code Online (Sandbox Code Playgroud)
其中 getImage 是一个返回 UIImage 的异步函数,您可以根据需要创建该函数。
我将以我的功能为例向您展示。
func getImage() async -> UIImage? {
guard let url = URL(string: BASE_URL + "cards/5fd36d31937b6b32d47ad9db/attachments/5fd36d31937b6b32d47ad9de/download") else {
fatalError("Missing URL")
}
var request = URLRequest(url: url)
request.addValue("OAuth oauth_consumer_key=\"\(API_KEY)\", oauth_token=\"\(ACCOUNT_TOKEN)\"", forHTTPHeaderField: "Authorization")
do {
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching attchment") }
return UIImage(data: data)
} catch {
return nil
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1981 次 |
| 最近记录: |