How should async downloading images work with waterfall's cgsize?
rlam3 opened this issue · 10 comments
How should async downloading images work with waterfall's cgsize?
Should we be doing async downloads inside cgsize ?
Should we be doing async downloads inside cgsize ?
I don't understand your question.
Your server should provide images information to you, includes images size.
Does it answer your question?
This requires there to be a size that needs to be returned correct?
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
So prior to the images being loaded the size must be returned for the cell. And in order to obtain the size, we must first asynchronously download the image to obtain the size to be returned here.
My question is ... how should we be handling the async download of images to obtain size to later be returned by CGSize. And because it is async, how should the completion handling look like within that particular code block. Ex. I'm using alamofireimage to obtain the image, but prior to alamofireimage finish obtaining the image, it will return nil because alamofire is async.
I assume that you get the image URLs from your server. If so, then your server can send image size and URL information together.
If your server don't know image size… sorry but I don't think there is a good way to solve the problem.
@chiahsien When you say the server should be sending the image size. In my particular case, I'm using AWS S3 to host the images. Correct me if I'm wrong, you're saying that I should be doing a full json request with s3 url in an external api to get the size every time it loads each cell?
No, what I'm saying is something like this:
- You send an API call to your server to get image information.
- Server responses a JSON like this
[
{
ImageURL: "http://xxx.xxx/abc.jpg",
width: 128,
height: 256
},
{
}
]
- Now you get every images size, so you can decide every cell size. Then you can download image when cell is going to be displayed.
It doesn't matter where you host the images, what is matter is that if your server knows image size.
Yes, this was pretty much along the lines I was saying. But only problem is these async alamofire tasks are handled asynchronously and the CGSize is called before we can obtain the results form the async commands...
Before "cellForItemAtIndexPath" can run to deqeue and configure the cell with images and data etc... "sizeForItemAtIndexPath" runs before that. Therefore it is not helpful.
Even If we were to run an async alamofire request to obtain a JSON. We still require it to return something prior to async command finishing the following func. And usually this will return nil.
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath;
@rlam3 One way you could handle that case is by returning 0 cells from numberOfItemsInSection
for the relevant section and once the async request(s) come back, you can store the results somewhere and reload the collectionView
. Then you would access the results synchronously in sizeForItemAtIndexPath
.
Another way might be to return a default size, and once the request comes back for the cell, reload that cell, though that can be a bit jumpy since it would lay them out once, and then have to update it again.
hard code:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath)
let imageView = cell.viewWithTag(100) as! UIImageView
imageView.layer.cornerRadius = 4.0
imageView.clipsToBounds = true
let json = jsonOfCourses[indexPath.row]
let json = jsonOfCourses[indexPath.row]
if let urlStr = json["pictureUrl"].string, urlStrEncode = urlStr.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()), url = NSURL(string: urlStrEncode) {
imageView.sd_setImageWithURL(url) { (img, err, nil, url) in
self.collectionView.reloadItemsAtIndexPaths([indexPath])
}
} else {
print("url nil, pls check api")
}
return cell
}
// MARK: WaterfallLayoutDelegate
func collectionView(collectionView: UICollectionView, layout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
var imgView: UIImageView!
var itemSize = CGSizeMake(0, 0)
if let subViews = collectionView.cellForItemAtIndexPath(indexPath)?.contentView.subviews {
for view in subViews {
if view.tag == 100 {
imgView = view as! UIImageView
}
}
}
if imgView != nil {
if let img = imgView.image {
let imgHeight = img.size.height
let imgWidth = img.size.width
let scale = imgHeight/imgWidth
let itemWidth = (CGRectGetWidth(self.view.frame) - 30)/2
let itemHeight = scale*itemWidth + 24
itemSize = CGSizeMake(itemWidth, itemHeight)
}
}
return itemSize
}
bug: when you scroll the screen, items will re-layout, jump here and there
@leancmscn I've taken the approach of letting swift app know exactly what size the image is supposed to be. However, my problem now is how to annotate the image with necessary captions. Any ideas for this would be appreciated! Thanks!
@Abhishekg219
You should provide actual image sizes, instead of default size.