I'm using the Photos framework to display user pictures in an iOS application I'm working on.
For reference, I uploaded a few images to imgur, where you can find the original images I'm testing with, and screenshots I took of an UIImageView with pink background displaying the fetched images with "Aspect Fit" configuration. Here's the link: https://i.sstatic.net/beugu.jpg
I'm using two test scenarios: first, a landscape image, whose imageOrientation
is UIImageOrientation.Up
(0); secondly, a portrait image, with UIImageOrientation.Right
(3). You can check in the documentation (I would put the link here, but I don't have enough reputation, sorry).
The iPhone 4S (iOS 8.1.2) displays the landscape image in a landscape layout inside the pink UIImageView, and the portrait with a portrait layout, as expected. But the iPhone 5 and the 6 Plus (both iOS 8.3) display both images in a landscape layout.
This is the code that I'm using to request the images:
var targetSize = CGSizeMake(1000, (CGFloat(asset.pixelHeight) / CGFloat(asset.pixelWidth)) * 1000)
var options = PHImageRequestOptions()
options.version = PHImageRequestOptionsVersion.Current
options.deliveryMode = PHImageRequestOptionsDeliveryMode.HighQualityFormat
options.resizeMode = PHImageRequestOptionsResizeMode.Exact
options.networkAccessAllowed = true
PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: targetSize, contentMode: PHImageContentMode.AspectFill, options: options) { (result: UIImage!, info) -> Void in
if !info[PHImageResultIsDegradedKey]!.boolValue {
println("asset.pixelWidth: \(asset.pixelWidth)")
println("asset.pixelHeight: \(asset.pixelHeight)")
println("targetSize: \(targetSize)")
println("result.size: \(result.size)")
println("result.imageOrientation: \(result.imageOrientation.rawValue)")
completion(result)
}
}
Here are the logs I get when executing tests with those two images:
Case 1: iPhone 4S (8.1.2)
Landscape:
asset.pixelWidth: 3264
asset.pixelHeight: 2448
targetSize: (1000.0, 750.0)
result.size: (1000.0, 750.0)
result.imageOrientation: 0
Portrait:
asset.pixelWidth: 2448
asset.pixelHeight: 3264
targetSize: (1000.0, 1333.33)
result.size: (1000.0, 1332.0)
result.imageOrientation: 3
Case 2: iPhone 5 (8.3)
Landscape:
asset.pixelWidth: 3264
asset.pixelHeight: 2448
targetSize: (1000.0, 750.0)
result.size: (1000.0, 750.0)
result.imageOrientation: 0
Portrait:
asset.pixelWidth: 2448
asset.pixelHeight: 3264
targetSize: (1000.0, 1333.33)
result.size: (1334.0, 1000.0)
result.imageOrientation: 3
Case 3: iPhone 6 Plus (8.3)
Landscape:
asset.pixelWidth: 3264
asset.pixelHeight: 2448
targetSize: (1000.0, 750.0)
result.size: (1000.0, 750.0)
result.imageOrientation: 0
Portrait:
asset.pixelWidth: 2448
asset.pixelHeight: 3264
targetSize: (1000.0, 1333.33333333333)
result.size: (1334.0, 1000.0)
result.imageOrientation: 3
You can see by the logs that the iOS 8.1.2 version respects the targetSize
specified for both images, but the 8.3 versions are fetching images with inverted dimensions (requested (1000.0, 1333.33)
but received (1334.0, 1000.0)
).
Right now, it seems like my only option is to check which iOS version is running, and then invert the parameters when requesting an image with rotated orientations (I only tested with UIImageOrientation.Right
, but there's also .Left
, .LeftMirrored
and .RightMirrored
).
Unfortunately, the PHAsset
doesn't contain the image's orientation. I can only know it after requesting the image to the PHImageManager
, so I'll have to make two requests for those images, one to obtain the orientation and another to get the correct image.
Is that an issue with my logic? Or is that a bug in iOS? If it's a bug, which one is the correct behaviour, the iOS 8.3's one or the older versions'? Can anyone offer other options to handling with this problem?
Sorry for the long post, and for the bad English. Thanks in advance!
I made a bizarre workaround:
func getAssetImage(asset: PHAsset, completion: (UIImage?) -> Void) {
getAssetImage(asset, firstTry: true, completion: completion)
}
private func getAssetImage(asset: PHAsset, firstTry: Bool, completion: (UIImage?) -> Void) {
let targetSize = CGSizeMake(1000, (CGFloat(asset.pixelHeight) / CGFloat(asset.pixelWidth)) * 1000)
let options = PHImageRequestOptions()
options.version = PHImageRequestOptionsVersion.Current
options.deliveryMode = PHImageRequestOptionsDeliveryMode.HighQualityFormat
options.resizeMode = PHImageRequestOptionsResizeMode.Exact
options.networkAccessAllowed = true
PHImageManager.defaultManager().requestImageForAsset(asset, targetSize: targetSize, contentMode: PHImageContentMode.AspectFill, options: options) { (result: UIImage?, info) -> Void in
let degraded = info?[PHImageResultIsDegradedKey] as? NSNumber
if degraded != nil && !degraded!.boolValue {
print("asset.pixelWidth: \(asset.pixelWidth)")
print("asset.pixelHeight: \(asset.pixelHeight)")
print("targetSize: \(targetSize)")
print("result.size: \(result!.size)")
print("result.imageOrientation: \(result!.imageOrientation.rawValue)")
if (targetSize.width - targetSize.height) * (result!.size.width - result!.size.height) < 0 {
if firstTry {
print("Resulting image's dimensions are not in accordance with target size. Reversing dimensions!")
self.getAssetImage(asset, firstTry: false, completion: completion)
} else {
print("Resulting image's dimensions are still not in accordance with target size. Giving up!")
completion(nil)
}
} else {
completion(result)
}
}
}
}
The performance of this solution is quite questionable, but it was one of the most-reliable ways to deal with this problem.