iOS 11下UIImagePickerControllerEditedImage裁剪图片偏移问题求助
iOS 11 UIImagePickerController 裁剪图片偏移问题修复方案
我之前也碰到过这个一模一样的坑——在iOS 11设备上用开启allowsEditing的UIImagePickerController选图裁剪后,得到的图片会莫名向下偏移,哪怕裁剪框和iOS 10里摆的位置完全一致,最终结果还是不对。这确实是iOS 11中UIImagePickerController的一个官方bug,你提交的bug报告也印证了这一点,可能很多开发者没注意到是因为没去严格对比跨版本的裁剪效果,或者在后续iOS版本里部分场景被修复了,但在特定场景下这个问题还是会出现。
你的代理实现是完全标准的操作,问题出在系统返回的UIImagePickerControllerEditedImage上,我们可以通过两种方式修复:
方法一:快速修正偏移量
针对已知的向下偏移问题,我们可以手动调整图片的绘制区域,把偏移的部分拉回来:
// 修正iOS 11裁剪图片的偏移问题 func fixEditedImageOffset(_ image: UIImage) -> UIImage? { guard #available(iOS 11.0, *) else { return image // iOS 10及以下无需修复 } let scale = image.scale let imageSize = image.size // 创建和原图一致的上下文 UIGraphicsBeginImageContextWithOptions(imageSize, false, scale) defer { UIGraphicsEndImageContext() } // 计算向下偏移的距离,反向绘制修正 let offsetY = (imageSize.height - CGFloat(image.cgImage!.height) / scale) / 2 let correctDrawRect = CGRect(x: 0, y: -offsetY, width: imageSize.width, height: imageSize.height) image.draw(in: correctDrawRect) return UIGraphicsGetImageFromCurrentImageContext() }
然后在你的代理方法里替换成修正后的图片:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let editedImage = info[.editedImage] as? UIImage else { picker.dismiss(animated: true) return } // 应用修复 let fixedImage = fixEditedImageOffset(editedImage) ?? editedImage imageView.image = fixedImage picker.dismiss(animated: true, completion: nil) }
方法二:手动重新裁剪(更可靠)
如果想彻底避开系统的bug,我们可以放弃系统返回的裁剪后图片,改用原始图片结合系统提供的裁剪区域,自己手动完成裁剪操作,这样兼容性更好:
首先给UIImage加个扩展,处理图片方向的坐标转换:
extension UIImage { // 转换坐标系以适配图片的orientation func transformForOrientation() -> CGAffineTransform { var transform = CGAffineTransform.identity switch imageOrientation { case .down, .downMirrored: transform = transform.translatedBy(x: size.width, y: size.height) transform = transform.rotated(by: .pi) case .left, .leftMirrored: transform = transform.translatedBy(x: size.width, y: 0) transform = transform.rotated(by: .pi / 2) case .right, .rightMirrored: transform = transform.translatedBy(x: 0, y: size.height) transform = transform.rotated(by: -.pi / 2) case .up, .upMirrored: break @unknown default: break } // 处理镜像翻转的情况 switch imageOrientation { case .upMirrored, .downMirrored: transform = transform.translatedBy(x: size.width, y: 0) transform = transform.scaledBy(x: -1, y: 1) case .leftMirrored, .rightMirrored: transform = transform.translatedBy(x: size.height, y: 0) transform = transform.scaledBy(x: -1, y: 1) default: break } return transform } }
然后修改你的代理方法,手动裁剪:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let originalImage = info[.originalImage] as? UIImage, let cropRect = info[.cropRect] as? CGRect else { picker.dismiss(animated: true) return } // 把系统返回的裁剪区域转换到原始图片的坐标系 let transformedCropRect = cropRect.applying(originalImage.transformForOrientation()) // 裁剪原始图片的CGImage guard let croppedCGImage = originalImage.cgImage?.cropping(to: transformedCropRect) else { picker.dismiss(animated: true) return } // 生成最终的UIImage,保留原始的scale和orientation let fixedImage = UIImage(cgImage: croppedCGImage, scale: originalImage.scale, orientation: originalImage.imageOrientation) imageView.image = fixedImage picker.dismiss(animated: true, completion: nil) }
总结
第一种方法适合快速解决已知的偏移问题,代码量小;第二种方法更稳妥,完全绕过了系统的bug,适合需要长期兼容iOS 11的场景。这个bug确实是iOS 11的系统问题,如果你已经向Apple提交了bug报告,后续的iOS版本可能会修复,但目前兼容iOS 11的话,还是用上述方案比较靠谱。
内容的提问来源于stack exchange,提问作者Aivars
相关产品推荐
相关产品推荐

