Sometimes images picked from the photo album with UIImagePickerController are cropped differently than how the user wants to crops it. This happens in approx. 1 of 50 image uploads.
When it happens the images are always cropped to a part of the image from the top left corner. Here is an example image with (1) showing in the red rectangle what the user supposedly selects to crop and (2) what image ends up on the server.
The selection in (1) is assumptive because it's unknown how the users exactly positions the crop and it was not possible to reproduce this incorrect cropping yet. It has only been observed with the live app. Some users tried to upload the same image multiple times always with the same incorrect crop and eventually complained, so it's not that users deliberately crop images like this.
Some users tried to upload different images and all of them were incorrectly cropped.
Here is the code (simplified but nothing more happens to the image):
class ImagePicker {
private let imagePicker = UIImagePickerController()
func showPicker() {
imagePicker.sourceType = .PhotoLibrary
imagePicker.mediaTypes = [kUTTypeImage as String]
imagePicker.allowsEditing = true
imagePicker.delegate = delegate
imagePicker.modalPresentationStyle = .OverFullScreen
parentViewController.presentViewController(imagePicker, animated: true, completion: nil)
}
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
uploadImage(image)
}
picker.dismissViewControllerAnimated(true, completion: nil)
}
func uploadImage(image: UIImage) {
let imageData = UIImageJPEGRepresentation(image, 0.75)!
let imageFile = PFFile(name: "image.png", data: imageData)
// Upload to Open Source Parse Server which stores the image in an Amazon S3 bucket.
let imageObject = PFObject(className: "ImageClass")
imageObject(imageFile, forKey: "imageFile")
imageObject.saveInBackground()
}
}
Does anyone know why this happens?
Update:
I was able to reproduce the issue on an iPad, I will update here what the outcome was.
Update:
The issue occurred only on iPads so it is presumably related to a bug in the UIImagePickerViewController when cropping an image.
I had the same issue. In my case, i solved it by change content mode of my imageView. It was set to "scaleAspectFill". i just removed it and my image become cropped right way.
Related
When I use PHImageManager() to retrieve an image from the photo library, the image is converted from a jpeg image to a png image, and the image is degraded.
How can I retrieve the image without image degradation?
Is it a standard specification that the extension is changed from jpeg to png?
If anyone knows, please respond.
Here is a sample code I made.
img is returned as png.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let assets: PHFetchResult = PHAsset.fetchAssets(with: .image, options: nil)
let mg: PHImageManager = PHImageManager()
guard let asset = assets.lastObject else {
return
}
let options = PHImageRequestOptions()
options.isNetworkAccessAllowed = true
options.deliveryMode = .highQualityFormat
mg.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: options, resultHandler: { image, info in
let img = image
})
}
}
When requesting a high quality image, the result handler of requestImage may be called initially with a low quality version of the image, then later called again with the high quality version.
You can check this by inspecting the PHImageResultIsDegradedKey of the info dictionary parameter of the result handler, for example:
mg.requestImage(for: asset, targetSize: PHImageManagerMaximumSize, contentMode: .aspectFit, options: options, resultHandler: { image, info in
if info?[PHImageResultIsDegradedKey] as? Bool == true {
print("Degraded image returned, will wait for high quality image")
return
}
let img = image
})
From the documentation for PHImageManager.requestImage:
[...] 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.) When the high-quality image is ready, Photos calls your result handler again to provide it. If the image manager has already cached the requested image at full quality, Photos calls your result handler only once. The PHImageResultIsDegradedKey key in the result handler’s info parameter indicates when Photos is providing a temporary low-quality image.
I have Swift code that uses the UIImagePickerController to have the user select a photo, a CIFilter is applied, then that UIImage is applied to a UIImageView. The problem is that I have no idea how make that image be in the correct orientation shown in the UIImagePickerController or in the Photos app. It's independent of the UIImage's imageOrientation. For instance, two different photos imported with a imageOrientation of "0" that are both portrait can resolve as a new UIImage with two completely different orientations. Maybe this is determined by EXIF information? Maybe it's both? That's why it's confusing.
Ultimately, I just want to have this image imported in the orientation shown in the UIImagePickerController or Photos app. If they can do it, there's some way I can.
#objc func imagePickerController(_ picker: UIImagePickerController!, didFinishPickingImage image: UIImage!, editingInfo: NSDictionary!) {
let selectedImage : UIImage = image
print("selectedImage.imageOrientation = \(selectedImage.imageOrientation.rawValue)")
let isPortrait:Bool = image.size.height > image.size.width ? true : false
dismiss(animated: true, completion: { () in
DispatchQueue.main.async {
self.enableButtons(enabled:false)
self.activityTextStatus?.setText("Preparing", center:self.view.center, addToView:self.loadingContainer, setRotation:true)
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.exportImagesToPreview(capturedImage: selectedImage,
orientation:nil,
isPortrait: isPortrait)
// here I apply an effect to the image,
// and it may be in the incorrect orientation
}
})
}
I'm using SDWebImage for my Swift. I can't figure out why (frankly, HOW) it's not working. there is no image in imageview.
so this is what i'm trying. parse query PFFile(s) (url converted) stored in a var urlArray:[URL] = []
i have a UIScrollView with UIImageView inside.
in short: query parse - download PFFile URLs to an array (append).
for index in 0..<self.urlArray.count {
var frame = CGRect.zero
frame.origin.x = self.scrollView.frame.size.width * CGFloat(index)
frame.size = self.scrollView.frame.size
self.scrollView.isPagingEnabled = true
let subView = UIImageView(frame: frame)
subView.sd_setImage(with: self.urlArray[index], placeholderImage: #imageLiteral(resourceName: "profile"))
//subView.contentMode = .scaleAspectFit
self.scrollView.addSubview(subView)
}
first url image appears ok. then no pagination & no error (array numbers OK) just nothing apears but first pic. used this : imageview.sd_setImage(with: self.urlArray[index]) is this wrong approach?
thanks
I don't see the code where you update the contentSize of the UIScrollView. Unless you do that, the scroll view won't scroll for you. The additional image views may be there, but hidden outside of the scroll view's current bounds.
sdWebImage is a library using for image caching.
If you wanna detect error for downloading image by sdWebImage then you apply below code and track response from server.
cell.imageView.sd_setImageWithURL(NSURL (string: sendImgUri!), placeholderImage: UIImage (named: "pro.png"), options: .CacheMemoryOnly, progress: { (i, m) in
}, completed: { (image, error, chachType, url) in
if(error != nil)
{
print(error)
}
else if(image != nil)
{
print(image)
}
})
Working on a pdf photo report app, and struggling with the low image quality in the pdfs that are generated.
func drawImage(index: Int, rectPos: Int) {
let image = getImage(index)
let xPosition = CGFloat(rectArray[rectPos][0])
let yPosition = CGFloat(rectArray[rectPos][1])
image.drawInRectAspectFill(CGRectMake(xPosition, yPosition, 325, 244))
}
func getImage(index: Int) -> UIImage {
var thumbnail = UIImage()
if self.photoAsset.count != 0 {
let initialRequestOptions = PHImageRequestOptions()
initialRequestOptions.resizeMode = .Exact
initialRequestOptions.deliveryMode = .HighQualityFormat
initialRequestOptions.synchronous = true
PHImageManager.defaultManager().requestImageForAsset(self.photoAsset[index], targetSize: CGSizeMake(325, 244), contentMode: PHImageContentMode.Default, options: initialRequestOptions, resultHandler: { (result, info) -> Void in
thumbnail = result!
})
}
return thumbnail
}
I then use these functions to grab the image and place it into a position on a page after UIGraphicsBeginPDFPageWithInfo(page, nil)...
I'm using BSImagePicker pod to get the images.
And finally my photoAsset is just an array of PHAsset photos that is generated after the user selects the images from the pod's CollectionView...
So far I tried all the settings for the initialRequestOptions.deliveryMode... highquality doesn't seem to make images any better.
What am I doing wrong here? Thanks!
Changing the target resolution when requesting image sets the minimum resolution of the image you are going to use.
instead of:
PHImageManager.defaultManager().requestImageForAsset(self.photoAsset[index], targetSize: CGSizeMake(325, 244)...
I simply doubled the size of the image and the image that is drawn to the pdf is better quality.
PHImageManager.defaultManager().requestImageForAsset(self.photoAsset[index], targetSize: CGSizeMake(650, 488)...
I am making an aracade-style game and when the player loses I give them the option to share their score via the iOS share sheet. What I want to know is, how can I have them share a screenshot taken right when they die along with some text. I already know how to make it so that they share text but I want the screenshot as well. I set it up like this so that the game takes a screenshot right when the player dies:
func screenShotMethod() {
//Create the UIImage
UIGraphicsBeginImageContext(view!.frame.size)
view!.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//Save it to the camera roll
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
println("screenshot")
}
Then I run this function at the GameOver Sequence like this:
if gameOver == 0{
gameOver = 1
***screenShotMethod()***
movingObjects.speed = 0
movingObjects.removeFromParent()
backgroundMusicPlayer.stop()
Now what I want to be able to do is access this screenshot so that it can be used in the sharing option, but deleted as soon as the player hits replay if the player doesn't share that score. Right now I have sharing set up like this:
if shareButton.containsPoint(location){
UIGraphicsBeginImageContext(view!.frame.size)
view!.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
//Save it to the camera roll
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
println("screenshot")
var postImage = UIImage(named: "\(image)")
socialShare(sharingText: "I just got \(score) points in Deez Nuts! Bet you can't beat that! #DeezNuts", sharingImage: UIImage(named: "\(postImage)"), sharingURL: NSURL(string: "http://itunes.apple.com/app/"))
}
Please be specific and straightforward because I am new to developing apps. Also I am using Swift if you didn't already notice. Thank you very much.
You just need to delete this line
var postImage = UIImage(named: "\(image)")
Because image is already an UIImage so just use sharingImage: image
socialShare(sharingText: "I just got \(score) points in Deez Nuts! Bet you can't beat that! #DeezNuts", sharingImage: image , sharingURL: NSURL(string: "http://itunes.apple.com/app/")!)