gpt4 book ai didi

异步下载图像时,SwiftUI 和 Combine 工作不流畅

转载 作者:搜寻专家 更新时间:2023-11-01 06:51:09 26 4
gpt4 key购买 nike

当我尝试使用 SwiftUI 和 Combine 异步下载图像时,它工作正常。然后,我尝试将其实现到一个动态列表中,我发现只有一行(最后一行)会正确显示,其他单元格中的图像丢失。我已经用断点跟踪代码,我确信图像下载过程在其他人中是成功的,但只有最后一行会触发 @ObjectBinding 来更新图像。请检查我的示例代码,如果有任何错误,请告诉我。谢谢!

struct UserView: View {
var name: String
@ObjectBinding var loader: ImageLoader

init(name: String, loader: ImageLoader) {
self.name = name
self.loader = loader
}

var body: some View {
HStack {
Image(uiImage: loader.image ?? UIImage())
.onAppear {
self.loader.load()
}
Text("\(name)")
}
}
}

struct User {
let name: String
let imageUrl: String
}

struct ContentView : View {
@State var users: [User] = []
var body: some View {
NavigationView {
List(users.identified(by: \.name)) { user in
UserView(name: user.name, loader: ImageLoader(with: user.imageUrl))
}
.navigationBarTitle(Text("Users"))
.navigationBarItems(trailing:
Button(action: {
self.didTapAddButton()
}, label: {
Text("+").font(.system(size: 36.0))
}))
}
}

func didTapAddButton() {
fetchUser()
}

func fetchUser() {
API.fetchData { (user) in
self.users.append(user)
}
}
}

class ImageLoader: BindableObject {

let didChange = PassthroughSubject<UIImage?, Never>()

var urlString: String
var task: URLSessionDataTask?
var image: UIImage? = UIImage(named: "user") {
didSet {
didChange.send(image)
}
}

init(with urlString: String) {
print("init a new loader")
self.urlString = urlString
}

func load() {
let url = URL(string: urlString)!
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
if error == nil {
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
}
task.resume()
self.task = task
}

func cancel() {
if let task = task {
task.cancel()
}
}
}

class API {
static func fetchData(completion: @escaping (User) -> Void) {
let request = URLRequest(url: URL(string: "https://randomuser.me/api/")!)
let task = URLSession.shared.dataTask(with: request) { (data, _, error) in
guard error == nil else { return }

do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any]
guard
let results = json!["results"] as? [[String: Any]],
let nameDict = results.first!["name"] as? [String: String],
let pictureDict = results.first!["picture"] as? [String: String]
else { return }

let name = "\(nameDict["last"]!) \(nameDict["first"]!)"
let imageUrl = pictureDict["thumbnail"]
let user = User(name: name, imageUrl: imageUrl!)
DispatchQueue.main.async {
completion(user)
}
} catch let error {
print(error.localizedDescription)
}
}
task.resume()
}
}

无论列表中有多少项,每张图片都应成功下载。

最佳答案

@ObjectBinding 中似乎存在错误。我不确定,我还不能确认。我想创建一个最小的示例代码来确定,如果是这样,请向 Apple 报告错误。似乎有时 SwiftUI 不会使 View 无效,即使它所基于的 @ObjectBinding 调用了 didChange.send() 也是如此。我发布了我自己的问题 ( @BindableObject async call to didChange.send() does not invalidate its view (and never updates) )

与此同时,我尽可能地尝试使用 EnvironmentObject,因为错误似乎并不存在。

然后您的代码只需很少的更改即可工作。不使用 ObjectBinding,而是使用 EnvironmentObject:

enter image description here

用@EnvironmentObject 替换@ObjectBinding 的代码:


import SwiftUI
import Combine

struct UserView: View {
var name: String
@EnvironmentObject var loader: ImageLoader

init(name: String) {
self.name = name
}

var body: some View {
HStack {
Image(uiImage: loader.image ?? UIImage())
.onAppear {
self.loader.load()
}
Text("\(name)")
}
}
}

struct User {
let name: String
let imageUrl: String
}

struct ContentView : View {
@State var users: [User] = []
var body: some View {
NavigationView {
List(users.identified(by: \.name)) { user in
UserView(name: user.name).environmentObject(ImageLoader(with: user.imageUrl))
}
.navigationBarTitle(Text("Users"))
.navigationBarItems(trailing:
Button(action: {
self.didTapAddButton()
}, label: {
Text("+").font(.system(size: 36.0))
}))
}
}

func didTapAddButton() {
fetchUser()
}

func fetchUser() {
API.fetchData { (user) in
self.users.append(user)
}
}
}

class ImageLoader: BindableObject {

let didChange = PassthroughSubject<UIImage?, Never>()

var urlString: String
var task: URLSessionDataTask?
var image: UIImage? = UIImage(named: "user") {
didSet {
didChange.send(image)
}
}

init(with urlString: String) {
print("init a new loader")
self.urlString = urlString
}

func load() {
let url = URL(string: urlString)!
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
if error == nil {
DispatchQueue.main.async {
self.image = UIImage(data: data!)
}
}
}
task.resume()
self.task = task
}

func cancel() {
if let task = task {
task.cancel()
}
}
}

class API {
static func fetchData(completion: @escaping (User) -> Void) {
let request = URLRequest(url: URL(string: "https://randomuser.me/api/")!)
let task = URLSession.shared.dataTask(with: request) { (data, _, error) in
guard error == nil else { return }

do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any]
guard
let results = json!["results"] as? [[String: Any]],
let nameDict = results.first!["name"] as? [String: String],
let pictureDict = results.first!["picture"] as? [String: String]
else { return }

let name = "\(nameDict["last"]!) \(nameDict["first"]!)"
let imageUrl = pictureDict["thumbnail"]
let user = User(name: name, imageUrl: imageUrl!)
DispatchQueue.main.async {
completion(user)
}
} catch let error {
print(error.localizedDescription)
}
}
task.resume()
}
}

关于异步下载图像时,SwiftUI 和 Combine 工作不流畅,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56937872/

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