使用 CGImageCreateWithImageInRect 裁剪圖片,模擬器正常,真機上裁剪區域錯誤、圖片扭曲或崩潰。原因是忽略了 UIImage.scale(螢幕縮放比)和 UIImage.imageOrientation(圖片方向元數據)。
CGRect 單位是點,CGImage 單位是像素。真機 scale=2.0/3.0,模擬器通常 scale=1.0。UIImage 顯示時自動旋轉,但 CGImage 為原始數據未旋轉。CGImage 會導致區域偏移。
@implementation UIImage (Crop)
- (UIImage *)crop:(CGRect)cropRect {
// 1. 將點座標轉換為像素座標(考慮 scale)
cropRect = CGRectMake(
cropRect.origin.x * self.scale,
cropRect.origin.y * self.scale,
cropRect.size.width * self.scale,
cropRect.size.height * self.scale
);
// 2. 從 CGImage 裁剪(原始像素數據)
CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], cropRect);
if (!imageRef) return nil;
// 3. 創建新 UIImage,保持原 scale 和 orientation
UIImage *result = [UIImage imageWithCGImage:imageRef
scale:self.scale
orientation:self.imageOrientation];
CGImageRelease(imageRef);
return result;
}
@end
// 視圖座標系下的裁剪矩形(單位:點)
CGRect cropRect = CGRectMake(50, 50, 200, 200);
// 調用分類方法
UIImage *originalImage = ...; // 來自相機、相冊、網絡
UIImage *croppedImage = [originalImage crop:cropRect];
// 顯示或保存
imageView.image = croppedImage;
| 參數 | 含義 | 作用 |
|---|---|---|
| self.scale | 圖片縮放比(@2x=2.0, @3x=3.0) | 點座標轉像素座標乘數 |
| self.imageOrientation | 圖片方向 | 保持顯示方向正確,避免旋轉錯誤 |
| scale 參數 | 新圖片縮放比 | 傳 self.scale 保持清晰度一致 |
// 裁剪視圖可見區域對應的圖片部分
- (UIImage *)cropToViewRect:(UIView *)view inImageView:(UIImageView *)imageView {
// 1. 將視圖矩形轉換到圖片座標系
CGRect viewRectInImageView = [view convertRect:view.bounds toView:imageView];
// 2. 圖片在 ImageView 中的顯示矩形(考慮 contentMode)
CGRect imageDisplayRect = [self contentRectForImageInImageView:imageView];
// 3. 計算裁剪區域在圖片原始像素中的位置
CGFloat scaleX = self.size.width / imageDisplayRect.size.width;
CGFloat scaleY = self.size.height / imageDisplayRect.size.height;
CGRect cropRect = CGRectMake(
(viewRectInImageView.origin.x - imageDisplayRect.origin.x) * scaleX,
(viewRectInImageView.origin.y - imageDisplayRect.origin.y) * scaleY,
viewRectInImageView.size.width * scaleX,
viewRectInImageView.size.height * scaleY
);
return [self crop:cropRect];
}
UIImage 分類,項目中統一調用。scale 和 orientation 傳遞給新 UIImage。CGImageSourceCreateThumbnailAtIndex 避免全量解碼內存峰值。