gpt4 book ai didi

ios - SwiftUI @ObservedObject 不会在异步获取图像时更新

转载 作者:行者123 更新时间:2023-12-01 16:20:26 26 4
gpt4 key购买 nike

我有一个应用程序,它从远程 API 获取带有图像 URL 的项目列表,然后它必须从该项目位置内的给定 url 获取每个项目的图像。
问题是,当上下滚动并因此从 View 中删除列表项并将它们移回 View 时,它们确实会显示出来。然而,在初始加载时,它们将永远处于“加载”状态,直到移出和移入。
我的代码:

import SwiftUI

struct ContentView: View {

@EnvironmentObject var artObjectStore: ArtObjectStore
@State private var pageCount = 1
@State private var tappedLink: String? = nil
@Environment(\.imageCache) var cache: ImageCache

var body: some View {
NavigationView {
Form {

Section(header: Text("Art")) {

List {
ForEach(artObjectStore.artObjects, id: \.self) { artObject in
self.link(for: artObject)

}
Button(action: loadMore) {
Text("")
}
.onAppear {
DispatchQueue.global(qos: .background).asyncAfter(deadline: DispatchTime(uptimeNanoseconds: 10)) {
self.loadMore()
}
}
}
}
}
.navigationBarTitle("Art objects")
}
.onAppear(perform: loadMore)

}

func loadMore() {
pageCount += 1
artObjectStore.loadMore(pageCount)
}

private func link(for artObject: ArtObject) -> some View {
let selection = Binding(get: { self.tappedLink },
set: {
UIApplication.shared.endEditing()
self.tappedLink = $0
})

return NavigationLink(destination: DetailView(artObject: artObject, cache: self.cache),
tag: artObject.id,
selection: selection) {
HStack(alignment: .center) {
VStack(alignment: .leading){
Text("\(artObject.title)").font(.system(size: 12))
Text("\(artObject.principalOrFirstMaker)").font(.system(size: 9)).foregroundColor(.gray)
}
Spacer()
AsyncImage(
url: URL(string: artObject.headerImage.url)!,
cache: self.cache,
width: 200,
height: 50
)
}
}
}
}


extension UIApplication {
func endEditing() {
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}

图片容器:
import SwiftUI
import Combine
import Foundation

class ImageLoader: ObservableObject {

let objectWillChange = ObservableObjectPublisher()

private var cancellable: AnyCancellable?
@Published var image: UIImage? {
willSet {
objectWillChange.send()
}
}


private let url: URL

private var cache: ImageCache?

init(url: URL, cache: ImageCache? = nil) {
self.url = url
self.cache = cache
}

deinit {
cancellable?.cancel()
}

private func cache(_ image: UIImage?) {
image.map { cache?[url] = $0 }
}

func load() {
if let image = cache?[url] {
self.image = image
return
}

cancellable = URLSession.shared.dataTaskPublisher(for: url)
.map { UIImage(data: $0.data) }
.replaceError(with: nil)
.handleEvents(receiveOutput: { [weak self] in self?.cache($0) })
.receive(on: DispatchQueue.main)
.assign(to: \.image, on: self)


}

func cancel() {
cancellable?.cancel()
}
}


struct AsyncImage: View {
@ObservedObject private var loader: ImageLoader
private let width: CGFloat?
private let height: CGFloat?
@State var spin = false

init(url: URL, cache: ImageCache? = nil, width: CGFloat? = nil, height: CGFloat? = nil) {
loader = ImageLoader(url: url, cache: cache)
self.width = width
self.height = height
}

var body: some View {
image
.onAppear(perform: loader.load)
.onDisappear(perform: loader.cancel)
}

private var image: some View {
Group {
if loader.image != nil {
Image(uiImage: loader.image!)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: width, height: height)
} else {
Image("loadingCircle")
.resizable()
.frame(width: 20, height: 20)
.rotationEffect(.degrees(spin ? 360 : 0))
.animation(Animation.linear(duration: 0.8).repeatForever(autoreverses: false))
.onAppear() {
self.spin.toggle()
}
}
}
}
}

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) }
}
}


struct ImageCacheKey: EnvironmentKey {
static let defaultValue: ImageCache = TemporaryImageCache()
}

extension EnvironmentValues {
var imageCache: ImageCache {
get { self[ImageCacheKey.self] }
set { self[ImageCacheKey.self] = newValue }
}
}

我确实尝试在图像上添加 willSet,这似乎不起作用。你能帮助我吗?

最佳答案

以下所有测试均使用 Xcode 11.4/iOS 13.4

修改工作AsyncImage
我改了GroupVStack它开始更新

    private var image: some View {
VStack { // << here !!
if loader.image != nil {

修改后的工作图像加载器
class ImageLoader: ObservableObject {

@Published var image: UIImage?

private var cancellable: AnyCancellable?
private let url: URL
private var cache: ImageCache?

init(url: URL, cache: ImageCache? = nil) {
self.url = url
self.cache = cache
}

deinit {
cancellable?.cancel()
}

private func cache(_ image: UIImage?) {
self.image = image
image.map { cache?[url] = $0 }
}

func load() {
if let image = cache?[url] {
self.image = image
return
}

cancellable = URLSession.shared.dataTaskPublisher(for: url)
.map { UIImage(data: $0.data) }
.receive(on: DispatchQueue.main)
.replaceError(with: nil)
.sink(receiveValue: { [weak self] in self?.cache($0) })
}

func cancel() {
cancellable?.cancel()
}
}

关于ios - SwiftUI @ObservedObject 不会在异步获取图像时更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61442507/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com