Jonovono
Jonovono

Reputation: 2085

Photos Framework. requestImageForAsset returning two results. Can't set image view

So I am using the SwipeView library (https://github.com/nicklockwood/SwipeView) to show images using the Photos framework for iOS8.

However, when I call the requestImageForAsset I notice I am getting two results, a thumbnail size, and the bigger size that I want. However, the bigger image isn't loaded (it's called async I understand) in time to return, so it returns the small image.

This code might make more sense.

    func swipeView(swipeView: SwipeView!, viewForItemAtIndex index: Int, reusingView view: UIView!) -> UIView! {
            let asset: PHAsset = self.photosAsset[index] as PHAsset

    var imageView: UIImageView!

    let screenSize: CGSize = UIScreen.mainScreen().bounds.size
    let targetSize = CGSizeMake(screenSize.width, screenSize.height)


    var options = PHImageRequestOptions()
//        options.deliveryMode = PHImageRequestOptionsDeliveryMode.Opportunistic
    options.resizeMode = PHImageRequestOptionsResizeMode.Exact


    PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: targetSize, contentMode: .AspectFill, options: options, resultHandler: {(result, info)in
        println("huhuhuh")
        println(result.size)
        println(info)
        imageView = UIImageView(image: result)
    })
    println("setting view)
    return imageView
}

Here is the log output:

Enteredhuhuhuh
(33.5,60.0)
SETTING VIEW
huhuhuh
(320.0,568.0)

As you can see it returns the image view before the big image is recieved. How do I make it return this larger image so it's not showing the thumbnai?

Thanks.

Upvotes: 26

Views: 22626

Answers (8)

Patel Pankaj
Patel Pankaj

Reputation: 11

here is the best solution I had applied and it's working perfectly

let imageManager = PHImageManager.default()
        let requestOptions = PHImageRequestOptions()
    requestOptions.deliveryMode =  .highQualityFormat
    requestOptions.version = .current
    requestOptions.resizeMode = .exact
    requestOptions.isSynchronous = true

    imageManager.requestImage(for: self.assets.first!, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: nil, resultHandler: { image, info in
        if let info = info, info["PHImageFileUTIKey"] == nil {
            if let isDegraded = info[PHImageResultIsDegradedKey] as? Bool, isDegraded {
                //Here is Low quality image , in this case return
                print("PHImageResultIsDegradedKey =======> \(isDegraded)")
                return
            }
            //Here you got high resilutions image
            
        }
    })

Upvotes: 0

Hiren Panchal
Hiren Panchal

Reputation: 3023

Use below options, swift 3.0

publi var imageRequestOptions: PHImageRequestOptions{
let options = PHImageRequestOptions()
options.version = .current
options.resizeMode = .exact
options.deliveryMode = .highQualityFormat
options.isNetworkAccessAllowed = true
options.isSynchronous = true
return options}

Upvotes: 0

vaibby
vaibby

Reputation: 1275

try this will work for asynchronously too.

[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:target contentMode:PHImageContentModeAspectFill options:option resultHandler:^(UIImage *result, NSDictionary *info) {
    if ([info objectForKey:@"PHImageFileURLKey"]) {
      //your code
    }
}];

Upvotes: 0

O Fenômeno
O Fenômeno

Reputation: 455

resultHandler: block has info dictionary which may contain Boolean value for PHImageResultIsDegradedKey key, which indicates whether the result image is a low-quality substitute for the requested image.

Here's what documentation says:

PHImageResultIsDegradedKey: A Boolean value indicating whether the result image is a low-quality substitute for the requested image. (NSNumber)

If YES, the result parameter of your resultHandler block contains a low-quality image because Photos could not yet provide a higher-quality image. Depending on your settings in the PHImageRequestOptions object that you provided with the request, Photos may call your result handler block again to provide a higher-quality image.

Upvotes: 4

Mrunal
Mrunal

Reputation: 14128

Check the Apple Documentation for this method here. In that they are saying:

For an asynchronous request, Photos may call your result handler block more than once. Photos first calls the block to provide a low-quality image suitable for displaying temporarily while it prepares a high-quality image. (If low-quality image data is immediately available, the first call may occur before the method returns.)

It might be taking time, in your case for fetching original size image. Otherwise your code seems okay to me.

Upvotes: 2

Midhun MP
Midhun MP

Reputation: 107221

By default the requestImageForAsset works as asynchronous. So in your method the return statement will be executed even before the image is retrieved. So my suggestion is instead of returning the imageView, pass the imageView in that you need to populate the image:

func swipeView(swipeView: SwipeView!, viewForItemAtIndex index: Int, reusingView view: UIView!, yourImageView: UIImageView)
{
    let asset: PHAsset = self.photosAsset[index] as PHAsset

    var imageView: UIImageView! = yourImageView;

    let screenSize: CGSize = UIScreen.mainScreen().bounds.size
    let targetSize = CGSizeMake(screenSize.width, screenSize.height)


    var options = PHImageRequestOptions()
    options.resizeMode = PHImageRequestOptionsResizeMode.Exact

    PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: targetSize, contentMode: .AspectFill, options: options, resultHandler: {(result, info)in
        imageView.image = result;
    })
}

Note:

Another option is you can fire a notification with the result image from the resultHandler. But I prefer the above mentioned method.

Refer PHImageManager for more information.

Upvotes: 4

Tony
Tony

Reputation: 4591

Read header of PHImageManager class

If -[PHImageRequestOptions isSynchronous] returns NO (or options is nil), resultHandler may be called 1 or more times. Typically in this case, resultHandler will be called asynchronously on the main thread with the requested results. However, if deliveryMode = PHImageRequestOptionsDeliveryModeOpportunistic, resultHandler may be called synchronously on the calling thread if any image data is immediately available. If the image data returned in this first pass is of insufficient quality, resultHandler will be called again, asychronously on the main thread at a later time with the "correct" results. If the request is cancelled, resultHandler may not be called at all. If -[PHImageRequestOptions isSynchronous] returns YES, resultHandler will be called exactly once, synchronously and on the calling thread. Synchronous requests cannot be cancelled. resultHandler for asynchronous requests, always called on main thread

So, what you want to do is that you make resultHandler to be called synchronously

PHImageRequestOptions *option = [PHImageRequestOptions new];
option.synchronous = YES;

[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:target contentMode:PHImageContentModeAspectFill options:option resultHandler:^(UIImage *result, NSDictionary *info) {
        //this block will be called synchronously
}];

So your block will be called before ending your method

Good luck!

Upvotes: 91

sage444
sage444

Reputation: 5684

You shouldn't rewrite imageView inside block, just set image to it and everything should work as you expect. To avoid showing two images you can check the size of image before assigning.

var imageView =  UIImageView()
...
...
        PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: targetSize, contentMode: .AspectFill, options: options, resultHandler: {(result, info)in
                println("huhuhuh")
                println(result.size)
                println(info)
                if result.size.width > 1000 &&  result.size.height > 1000 { // add any size you want 
                     imageView.setImage(result)
                }
            })

Upvotes: 2

Related Questions