Reputation: 2085
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
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
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
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
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
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
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
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
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