首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

iOS 開發:UIImage 裁剪在真機上異常的原因與正確實現(考慮 scale 與 orientation)

     編輯:程式設計綜合問答

問題現象

使用 CGImageCreateWithImageInRect 裁剪圖片,模擬器正常,真機上裁剪區域錯誤、圖片扭曲或崩潰。原因是忽略了 UIImage.scale(螢幕縮放比)和 UIImage.imageOrientation(圖片方向元數據)。

根本原因分析

  • 座標系差異:CGRect 單位是點,CGImage 單位是像素。真機 scale=2.0/3.0,模擬器通常 scale=1.0。
  • 方向元數據:相機拍攝照片帶 EXIF 方向,UIImage 顯示時自動旋轉,但 CGImage 為原始數據未旋轉。
  • 上下文不匹配:直接用視圖座標裁剪 CGImage 會導致區域偏移。

正確解決方案:UIImage 分類方法處理 scale 與 orientation


@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 避免全量解碼內存峰值。
  • ✅ 測試覆蓋:@1x/@2x/@3x 設備、不同 orientation 圖片(相機拍攝、截圖、網絡下載)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved