在 ObservableObject 中调用多个函数
Posted
技术标签:
【中文标题】在 ObservableObject 中调用多个函数【英文标题】:Calling multiple functions in ObservableObject 【发布时间】:2020-12-24 06:24:43 【问题描述】:我之前问过一个问题,但我认为我不清楚自己要做什么。我创建了一个必须调用两个函数的ObservableObject
类。我需要首先为新 URL 调用 load
,然后将该 URL 设置为 loadImage
,但不确定如何正确调用它以在每个按钮操作中获取新图像。一个用户添加了一个函数不知道另一个是否完成的评论,我该如何解决这个问题?
目前它确实会在每次按下按钮时获取一个新的image,但我必须添加这个,但代码在我的内容视图中似乎不正确。
func load()
info.load()
info.loadImage(from: URL(string:info.cats.file)!)
我的ObservableObject
课程在下面。
class CatInfo : ObservableObject
@Published var cats: RandomCats = RandomCats(file: "https://purr.objects-us-east-1.dream.io/i/kef4p.jpg")
@Published var image: UIImage = UIImage(systemName:"exclamationmark")!
var cancellables: Set<AnyCancellable> = Set<AnyCancellable>()
var loaded: Bool = false
init(cats: RandomCats)
self.cats = cats
init()
load()
loadImage(from: URL(string: self.cats.file)!)
func load()
URLSession.shared
.dataTaskPublisher(for: URLRequest(url: URL(string: "http://aws.random.cat/meow")!))
.map(\.data)
.decode(type: RandomCats.self, decoder: JSONDecoder())
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: completion in
switch completion
case .finished:
break
case .failure(let error):
print(error.localizedDescription)
, receiveValue: data in
self.cats = data
print(self.cats.file)
)
.store(in: &self.cancellables)
func loadImage(from url: URL)
URLSession.shared
.dataTaskPublisher(for: URLRequest(url: url))
.map(\.data)
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: completion in
switch completion
case .finished:
break
case .failure(let error):
print(error.localizedDescription)
, receiveValue: data in
if let image = UIImage(data: data)
self.image = image
self.loaded = true
)
.store(in: &self.cancellables)
在我的内容视图中,我为按钮的操作创建了另一个调用 func load()
和 func loadImage(from url: URL)
的加载函数。
struct ContentView: View
@ObservedObject var info: CatInfo
public var defaultImage: UIImage = UIImage(systemName:"exclamationmark")!
var body: some View
VStack
if info.loaded
Image(uiImage: (info.image))
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 300, height:500)
else
Image(systemName:"exclamationmark")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 100, height:100)
Button(action: load, label:
Text("Randomize")
.font(.title2)
)
.padding(.all)
func load()
info.load()
info.loadImage(from: URL(string:info.cats.file)!)
【问题讨论】:
我不清楚你想要完成什么。load
函数和 loadImage
函数是如何相互关联的?您是否要从现有(即已检索)的 URL 加载图像,然后使用 load
更新 URL?还是相反:load
首先是新 URL,然后使用该 URL 到 loadImage
?正如现在编码的那样,这两个函数是相互独立编码的,同时是异步的。这意味着一个功能不知道另一个是否完成
我建议你重写这个问题:你想要实现什么行为,你观察到什么(即错误是什么),你在load
调用中得到了什么(我们现在只能猜测),并删除与问题无关的任何内容(例如所有#if os
的东西、样式等...)
我正在尝试相反的 load
新 URL,然后将该 URL 设置为 loadImage
。这正是我正在寻找的,抱歉措辞不正确。会更新的。
我理解你的问题,使用 KingFisher 很容易。但是我们也可以通过这种方式做到这一点。
@colecabral,看起来您找到了一种方法,尽管您将组合和回调混合在一起有点奇怪。一般来说,Combine 管道可以链接到异步调用序列,因此您可以创建一个既加载数据又加载图像的管道。
【参考方案1】:
由于您的图片依赖于您从互联网上获取的随机 url,因此您必须在获取图片 url 后调用图片加载函数。
1.删除 ContentView 中的 load()
并仅在按钮操作中调用 info.load()
。
2.调用 func loadImage(from URL(string:info.cats.file)!)
后 url 在load()
中接收值。
注意:您可以使用 KingFisherSwiftUi 轻松完成此操作,使用 KingFisher 您只需提供图片视图的 url,无需手动加载图片。
【讨论】:
最好不要使用任何第三方工具。我基本上还在学习基础知识。我想我设法解决了我的问题。 我的回答对你有帮助吗? 不幸的是它没有帮助。不过还是谢谢【参考方案2】:我对您的问题的理解是,您首先需要获取图片 URL,然后使用该 URL 来实际加载图片。
要使用两个异步函数来实现这一点,您不能一个接一个地同步调用它们。相反,第二个(依赖)函数只能在第一个函数完成时调用,这可能会在以后的某个未知时间发生。
通常,此用例通过回调(类似于您在已删除答案中所做的)或链接组合运算符来解决。
例如,如果你有这些功能:
func loadImageURL() -> AnyPublisher<URL, Error> ...
func loadImage(for url: URL) -> AnyPublisher<UIImage, Error> ...
然后您可以将它们链接起来以产生最终结果:
let cancellable = loadImageURL()
.flatMap url in
loadImage(for: url)
.catch _ in Empty() // ignore errors for simplicity
.sink image in
self.image = image
因此,您可以创建一个 func randomize() ..
来执行此操作,但它会不断生成新的订阅。或者,您可以使用 PassthroughSubject
并设置一个不断更新 image
属性的订阅:
class CatInfo: ObservableObject
@Published var image: UIImage? = nil
private let randomizeSubject = PassthroughSubject<Void, Never>()
private var cancellables: Set<AnyCancellable> = []
init()
randomizeSubject
.flatMap
loadImageURL().catch _ in Empty()
.flatMap url in
loadImage(for: url).catch _ in Empty()
.receive(on: DispatchQueue.main)
.sink [weak self] image in
self?.image = image
.store(in: &cancellables)
func randomize()
randomizeSubject.send(())
【讨论】:
谢谢你的详细解释,我试试看!我设法让它在没有组合的情况下工作,但我会看看我是否可以将它整合到我现在拥有的东西中。以上是关于在 ObservableObject 中调用多个函数的主要内容,如果未能解决你的问题,请参考以下文章
如何告诉 SwiftUI 视图绑定到多个嵌套的 ObservableObject
如何从我的 ObservableObject 类调用我的 View 结构中的方法
SwiftUI/组合监听 ObservableObject 中的多个发布者
调用 ObservableObject 类初始化器来更新 SwiftUI 中的 List