ios - 来自 URL 的图像未加载
问题描述
我正在尝试让屏幕女巫从 firebase 实时数据库中获取图像 url,然后从该 url 加载图像。问题是每当我调用函数 withc 从数据库中获取 url 图像没有加载并且全局变量 withc 保持 url 始终具有默认值
我的功能:
@State var imageURL: String = "none"
func getCurrentUserProfilePictureURL() {
let ref: DatabaseReference = Database.database().reference()
let userRef = ref.child("users").child(Auth.auth().currentUser?.uid ?? "noid")
// Get Current Profie Picture URL from Real-Time Database
userRef.observeSingleEvent(of: .value) { snapshot in
self.imageURL = snapshot.childSnapshot(forPath: "photoUrl").value as! String
} withCancel: { error in
print("ERROR: \(error.localizedDescription)")
}
}
我的图像视图:
VStack {
if let imageURLString = imageURLString, let url = URL(string: imageURLString) {
AsyncImage(url: url) { image in
image
} placeholder: {
placeholder
}
} else {
placeholder
}
}.onAppear {
getCurrentUserProfilePictureURL()
}
我的占位符:
var placeholder : some View {
Image("user_icon")
.frame(width: 120, height: 120, alignment: .center)
}
AsyncImage.swift:
//
// AsyncImage.swift
// iGrow Goals
//
// Created by George Sepetadelis on 27/8/21.
//
import Foundation
import SwiftUI
import UIKit
import Combine
struct AsyncImage<Placeholder: View>: View {
@StateObject private var loader: ImageLoader
private let placeholder: Placeholder
private let image: (UIImage) -> Image
init(
url: URL,
@ViewBuilder placeholder: () -> Placeholder,
@ViewBuilder image: @escaping (UIImage) -> Image = Image.init(uiImage:)
) {
self.placeholder = placeholder()
self.image = image
_loader = StateObject(wrappedValue: ImageLoader(url: url, cache: Environment(\.imageCache).wrappedValue))
}
var body: some View {
content
.onAppear(perform: loader.load)
}
private var content: some View {
Group {
if loader.image != nil {
image(loader.image!)
} else {
placeholder
}
}
}
}
protocol ImageCache {
subscript(_ url: URL) -> UIImage? { get set }
}
struct TemporaryImageCache: ImageCache {
private let cache = NSCache<NSURL, UIImage>()
subscript(_ key: URL) -> UIImage? {
get { cache.object(forKey: key as NSURL) }
set { newValue == nil ? cache.removeObject(forKey: key as NSURL) : cache.setObject(newValue!, forKey: key as NSURL) }
}
}
class ImageLoader: ObservableObject {
@Published var image: UIImage?
private(set) var isLoading = false
private let url: URL
private var cache: ImageCache?
private var cancellable: AnyCancellable?
private static let imageProcessingQueue = DispatchQueue(label: "image-processing")
init(url: URL, cache: ImageCache? = nil) {
self.url = url
self.cache = cache
}
deinit {
cancel()
}
func load() {
guard !isLoading else { return }
if let image = cache?[url] {
self.image = image
return
}
cancellable = URLSession.shared.dataTaskPublisher(for: url)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
.handleEvents(receiveSubscription: { [weak self] _ in self?.onStart() },
receiveOutput: { [weak self] in self?.cache($0) },
receiveCompletion: { [weak self] _ in self?.onFinish() },
receiveCancel: { [weak self] in self?.onFinish() })
.subscribe(on: Self.imageProcessingQueue)
.receive(on: DispatchQueue.main)
.sink { [weak self] in self?.image = $0 }
}
func cancel() {
cancellable?.cancel()
}
private func onStart() {
isLoading = true
}
private func onFinish() {
isLoading = false
}
private func cache(_ image: UIImage?) {
image.map { cache?[url] = $0 }
}
}
struct ImageCacheKey: EnvironmentKey {
static let defaultValue: ImageCache = TemporaryImageCache()
}
extension EnvironmentValues {
var imageCache: ImageCache {
get { self[ImageCacheKey.self] }
set { self[ImageCacheKey.self] = newValue }
}
}
解决方案
有几件事应该改变:
你只需要在
AsyncImage
你有一个有效的 URL 时加载你的 - 现在,你正试图从头开始加载,其中的值只是“none”您不应该调用
getCurrentUserProfilePictureURL
ininit
,因为init
在 SwiftUI 上View
可能会被调用多次,并且不应该用于繁重的工作或不应该多次运行的代码。相反,使用onAppear
.
struct ContentView: View {
@State private var imageURLString: String? = nil
var placeholder : some View {
Image("user_icon")
.frame(width: 120, height: 120, alignment: .center)
}
var body: some View {
VStack {
if let imageURLString = imageURLString, let url = URL(string: imageURLString) {
AsyncImage(url: url) { image in
image
} placeholder: {
placeholder
}
} else {
placeholder
}
}.onAppear {
getCurrentUserProfilePictureURL()
}
}
func getCurrentUserProfilePictureURL() {
let ref: DatabaseReference = Database.database().reference()
let userRef = ref.child("users").child(Auth.auth().currentUser?.uid ?? "noid")
// Get Current Profie Picture URL from Real-Time Database
userRef.observeSingleEvent(of: .value) { snapshot in
self.imageURL = snapshot.childSnapshot(forPath: "photoUrl").value as! String
} withCancel: { error in
print("ERROR: \(error.localizedDescription)")
}
}
}
在上面的代码中,imageURLString
是一个 Optional - 如果它没有值,则显示占位符图像。一旦它确实有一个值(即异步getCurrentUserProfilePictureURL
完成时),它就可以开始加载AsyncImage
根据评论,您正在使用某种自定义AsyncImage
实现。因此,您的该部分的代码可能需要看起来更像(猜测,因为我不知道实现是什么样的——这是基于您的原始代码):
AsyncImage(url: url,
placeholder: { placeholder },
image: { Image(uiImage: $0).resizable() })
推荐阅读
- java - 如何将 csv 文件转换为 ByteArrayResource
- c++ - 编译器是否被迫根据模板参数评估 if 表达式?
- visual-studio - VS2019 + Clang 无法打开'libboost_date_time-clangw10-mt-gd-x32-1_73.lib':没有这样的文件或目录
- mysql - 当数据集中 2 个数据表中的 1 个返回空行时,Crystal Report 不显示数据
- apache-spark - 将 KAFKA json 消息转换为案例类,并使用 RDD 方法使用 play json 将其保存到 Cassandra
- javascript - 如何使用地图功能的承诺在javascript中同步运行?
- python - 使用 python asyncio 运行两个无限任务
- python - 创建具有重复结构的字符串
- django - NameError:我的 Django 代码中未定义名称“ForeignKey”
- powershell - PowerShell:方法调用失败,因为 ... 不包含名为