我从服务器加载帖子,并且只想在图像全部加载时才显示它们。这是我试图做的:
postsRepository
.fetchPosts()
.flatMap { posts -> Observable<[Post]> in
return Observable.merge(posts.map({ /* Code that returns Observable<UIImage> */ }))
.takeLast(1)
.map { _ in posts }
}
但是,上面的这段代码加载图像两次:一次获取所有帖子,一次我将帖子绑定到表视图。如何处理?
这是一个有趣的...
let postsWithImages = postsRepository
.fetchPosts()
.flatMap { posts in
Observable.combineLatest(
posts
.map { $0.imageURL }
.map { URLSession.shared.rx.data(request: URLRequest(url: $0))
.map { UIImage(data: $0) }
.catchErrorJustReturn(nil)
}
)
.map { zip(posts, $0) }
}
.map { $0.map { (post: $0.0, image: $0.1) } }
如果您习惯于使用Zip2Sequence
,那么最后map
可能不是绝对必要的。
以上假设Post
具有imageURL: URL
属性。
让我们清理一下:
let postsWithImages = postsRepository
.fetchPosts()
.flatMap { posts in
Observable.combineLatest(
posts
.map { $0.imageURL }
.map { URLSession.shared.rx.image(for: $0) }
)
.map { zip(posts, $0) }
}
.map { $0.map { (post: $0.0, image: $0.1) } }
以上需要一个新的函数:
extension Reactive where Base == URLSession {
func image(for url: URL) -> Observable<UIImage?> {
return data(request: URLRequest(url: url))
.map { UIImage(data: $0) }
.catchErrorJustReturn(nil)
}
}
我想我会补充一些解释。正如我在文章中提到的, 在 RxSwift 中组合可观察量的配方,这里的魔力是使用 combineLatest
将[Observable<T>]
转换为Observable<[T]>
。
运算符获取 Observable 数组,订阅所有可观察量,等待它们全部完成,然后发出一个事件,其中包含收集的所有值的数组。(之后,每次更新其中一个项目时,它都会发出一个新数组,但这在这里无关紧要。