如何在Swift中实现UIImage的自由裁剪功能?
iOS 原生UIImage自定义裁剪功能实现方案
你需要实现的裁剪效果如下:
以下完全基于iOS系统API实现,无需引入任何第三方库,分为UI交互层实现和核心裁剪逻辑两部分:
一、裁剪交互UI实现
- 首先创建全屏半透明蒙层,使用
CAShapeLayer实现中间镂空效果,设置fillRule = .evenOdd,绘制路径时先绘制蒙层全屏区域,再叠加中间裁剪框区域,系统会自动保留裁剪框位置的透明效果 - 为裁剪框添加平移手势支持移动位置,添加捏合手势或四角拖动手势支持调整裁剪框大小,可根据需求设置是否固定裁剪比例
- 底层添加
UIImageView展示待裁剪原图,可根据需求添加手势支持用户缩放、拖动原图调整展示内容
二、核心裁剪逻辑实现
UIImage裁剪需要注意处理图片方向属性,否则会出现裁剪后内容旋转、错位的问题,以下是兼容图片方向的裁剪扩展:
extension UIImage { /// 裁剪图片到指定区域,自动适配图片原始方向 func cropped(to rect: CGRect) -> UIImage? { guard let cgImage = self.cgImage else { return nil } let scale = self.scale // 处理图片方向转换 var transform = CGAffineTransform.identity switch self.imageOrientation { case .down, .downMirrored: transform = transform.translatedBy(x: size.width, y: size.height).rotated(by: .pi) case .left, .leftMirrored: transform = transform.translatedBy(x: size.width, y: 0).rotated(by: .pi / 2) case .right, .rightMirrored: transform = transform.translatedBy(x: 0, y: size.height).rotated(by: -.pi / 2) default: break } // 处理镜像翻转 switch self.imageOrientation { case .upMirrored, .downMirrored: transform = transform.translatedBy(x: size.width, y: 0).scaledBy(x: -1, y: 1) case .leftMirrored, .rightMirrored: transform = transform.translatedBy(x: size.height, y: 0).scaledBy(x: -1, y: 1) default: break } // 创建裁剪上下文 guard let context = CGContext( data: nil, width: Int(rect.width * scale), height: Int(rect.height * scale), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: cgImage.colorSpace ?? CGColorSpaceCreateDeviceRGB(), bitmapInfo: cgImage.bitmapInfo.rawValue ) else { return nil } // 应用坐标转换后裁剪 context.concatenate(transform) context.draw(cgImage, in: CGRect(x: -rect.minX * scale, y: -rect.minY * scale, width: size.width * scale, height: size.height * scale)) guard let croppedCGImage = context.makeImage() else { return nil } return UIImage(cgImage: croppedCGImage, scale: scale, orientation: .up) } }
三、坐标转换注意事项
你在UI层拿到的裁剪框坐标是相对于UIImageView的坐标,需要先转换为图片本身的实际坐标再调用裁剪方法,避免因为UIImageView的contentMode缩放导致裁剪区域错位,以下是scaleAspectFit模式下的坐标转换示例:
/// 将UI层裁剪框坐标转换为图片本身的坐标 /// - Parameters: /// - cropRect: 裁剪框相对于UIImageView的坐标 /// - imageView: 展示原图的UIImageView /// - image: 待裁剪的原图 func convertCropRect(cropRect: CGRect, for imageView: UIImageView, image: UIImage) -> CGRect { let viewSize = imageView.bounds.size let imageSize = image.size // 计算scaleAspectFit模式下图片的实际缩放比例和偏移量 let scale = min(viewSize.width / imageSize.width, viewSize.height / imageSize.height) let displaySize = CGSize(width: imageSize.width * scale, height: imageSize.height * scale) let offsetX = (viewSize.width - displaySize.width) / 2 let offsetY = (viewSize.height - displaySize.height) / 2 // 转换为图片内部坐标 let actualX = (cropRect.minX - offsetX) / scale let actualY = (cropRect.minY - offsetY) / scale let actualWidth = cropRect.width / scale let actualHeight = cropRect.height / scale return CGRect(x: actualX, y: actualY, width: actualWidth, height: actualHeight) }
内容的提问来源于stack exchange,提问作者Abhishek K
相关产品推荐
相关产品推荐

