【发布时间】:2021-11-03 14:54:33
【问题描述】:
我正在尝试让屏幕女巫从 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 }
}
}
【问题讨论】:
-
这里没有足够的信息来帮助调试。如果在设置
self.imageURL之前添加print,它会打印什么吗?如果不是,它会在您的withCancel闭包中打印什么错误? -
@George 这个变量总是有默认值(“none”)
-
您需要显示调用 getCurrentUserProfilePictureURL 的位置。由于它是异步的,因此您需要一种方法,在获得有效 URL 之前不将 URL 发送到 AsyncImage。
-
@jnpdx 我在
init函数中调用它
标签: ios swift firebase-realtime-database swiftui