问题
I'm trying to implement Kingfisher
prefetch feature inside an Rxswift project. The problem is with these 2 function
collectionView.rx.prefetchItems
collectionView.rx.cancelPrefetchingForItems
The instruction at Kingfisher
github
is quite short
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.prefetchDataSource = self
}
extension ViewController: UICollectionViewDataSourcePrefetching {
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
let urls = indexPaths.flatMap { URL(string: $0.urlString) }
ImagePrefetcher(urls: urls).start()
}
}
How can we implement with Rxswift? anyway to get the models
and then the urls
of models
from array of indexpath
. Thank.
回答1:
I will walk you thru how I figured out the solution to help you figure out future solutions...
I'm assuming that the struct that holds the URL strings is called Item
and that you have an Observable<[Item]>
that you are currently using to load up the collection view. I'm also assuming that you only have one section in your collection.
First, we know that something needs to happen when the prefetchItemsAt
sends an event so we start with that:
let foo = collectionView.rx.prefetchItems
Now inspect the type of foo
to see that it is a ControlEvent<[IndexPath]>
. A ControlEvent is a kind of observable. We just need the items
part of the IndexPaths, so lets map
that:
let foo = collectionView.rx.prefetchItems
.map { $0.map { $0.item } }
(The double map is an unfortunate consequence of Swift not supporting higher kinded types) Now inspect the type of foo
. It is an Observable array of ints. Those ints are indexes into our items array. So we need access to the most recently emitted items:
(as above)
.withLatestFrom(items) { indexes, elements in
indexes.map { elements[$0] }
}
So withLatestFrom
is like combineLatest
except it only fires when the primary observable emits a value, not when the secondary observable does.
Now, inspecting the type of foo
will find that it's an Observable array of Items. The exact items who's urls' we want to send to the ImagePrefetcher. So we need to extract the urlStrings into URLs.
(as above)
.map { $0.compactMap { URL(string: $0.urlString) } }
And with that, we have the array of URLs we want ImagePrefetcher to consume. Since it consumes data, it needs to be wrapped it in a subscribe block.
(as above)
.subscribe(onNext: {
ImagePrefetcher(urls: $0).start()
})
At this point, foo
is a disposable so it just needs to be collected in our dispose bag... Here is the entire block of code.
collectionView.rx.prefetchItems
.map { $0.map { $0.item } }
.withLatestFrom(items) { indexes, elements in
indexes.map { elements[$0] }
}
.map { $0.compactMap { URL(string: $0.urlString) } }
.subscribe(onNext: {
ImagePrefetcher(urls: $0).start()
})
.disposed(by: bag)
One last bit to make everything clean... Everything between prefetchItems
and the subscribe
can be moved into the ViewModel if you have one.
The key takeaway here is that you can use the types to guide you, and you need to know what operators are available to manipulate Observables which you can find at http://reactivex.io/documentation/operators.html
来源:https://stackoverflow.com/questions/52803206/ios-rxswift-use-kingfisher-to-prefetch-cell-image