iOS开发基础3-图像处理详解:UIImage、UIImageView 与 UIViewContentMode

iOS 图像处理详解:UIImage、UIImageView 与 UIViewContentMode

在 iOS 开发中,UIImage 负责图像数据的加载与处理,UIImageView 负责图像的展示,UIViewContentMode 则控制内容在视图内的缩放与对齐方式。三者配合,覆盖了图像从加载到渲染的完整链路。


一、UIImage

UIImage 是图像数据的高层封装,管理图像的像素数据,并提供加载、缩放、裁剪等操作接口。

特点

  • 支持多种图片格式:PNG、JPEG、HEIC、GIF(静态首帧)等。
  • 多种加载方式:从 bundle 资源、文件路径、NSDataCGImage/CIImage 等创建。
  • 图像处理:支持拉伸、裁剪、缩放等操作。
  • 不可变对象UIImage 是不可变的,所有图像处理都会生成新的 UIImage 实例。

图片加载方式对比

方法 来源 缓存 适用场景
imageNamed: Bundle 资源 系统自动缓存 频繁使用的小图(图标、占位图)
imageWithContentsOfFile: 文件路径 无缓存 大图、偶尔使用的图(节省内存)
imageWithData: NSData 无缓存 网络下载后的数据、自定义数据
imageWithCGImage: / imageWithCIImage: 底层图像对象 无缓存 与 Core Graphics / Core Image 交互

重要区别imageNamed: 会将图片缓存在系统内存中,再次加载同名图片时直接从缓存读取,适合小图;但大图使用此方法会导致缓存占用过高,应改用 imageWithContentsOfFile:

用法示例

Objective-C

// 1. 从 Bundle 加载(带缓存,适合小图)
UIImage *imageNamed = [UIImage imageNamed:@"example.png"];

// 2. 从文件路径加载(无缓存,适合大图)
NSString *path = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"png"];
UIImage *imageFromFile = [UIImage imageWithContentsOfFile:path];

// 3. 从 NSData 加载(如网络下载后的数据)
// 注意:不要在主线程同步请求网络!此处仅演示 data → UIImage 的转换
NSData *imageData = [NSData dataWithContentsOfFile:path];
UIImage *imageFromData = [UIImage imageWithData:imageData];

// 4. 缩放图片(注意:使用 WithOptions 版本以支持 Retina 屏幕)
CGSize newSize = CGSizeMake(100, 100);
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0); // scale=0.0 自动匹配屏幕
[imageNamed drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

Swift

// 1. 从 Bundle 加载(带缓存)
let imageNamed = UIImage(named: "example.png")

// 2. 从文件路径加载(无缓存)
if let path = Bundle.main.path(forResource: "example", ofType: "png") {
    let imageFromFile = UIImage(contentsOfFile: path)
}

// 3. 从 Data 加载
if let data = try? Data(contentsOf: URL(fileURLWithPath: path)) {
    let imageFromData = UIImage(data: data)
}

// 4. 缩放图片(Swift 推荐使用 UIGraphicsImageRenderer)
let newSize = CGSize(width: 100, height: 100)
let renderer = UIGraphicsImageRenderer(size: newSize)
let resizedImage = renderer.image { _ in
    imageNamed?.draw(in: CGRect(origin: .zero, size: newSize))
}

异步网络加载图片

Objective-C

NSURL *url = [NSURL URLWithString:@"https://example.com/image.png"];
[[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    if (data && !error) {
        UIImage *image = [UIImage imageWithData:data];
        dispatch_async(dispatch_get_main_queue(), ^{
            self.imageView.image = image; // UI 更新必须在主线程
        });
    }
}] resume];

Swift

let url = URL(string: "https://example.com/image.png")!
URLSession.shared.dataTask(with: url) { data, _, error in
    if let data = data, error == nil, let image = UIImage(data: data) {
        DispatchQueue.main.async {
            self.imageView.image = image // UI 更新必须在主线程
        }
    }
}.resume()

底层实现

  • UIImage 是不可变对象,底层可以包装 CGImage(Core Graphics 位图)或 CIImage(Core Image 图像)。
  • 图像的实际像素数据由 CGImage 管理,UIImage 提供面向对象的高层接口。
  • UIImagescale 属性,表示图像的缩放因子(@1x/@2x/@3x),与屏幕 scale 配合决定实际渲染像素数。
  • UIImage 本身不负责显示,只负责持有和处理图像数据。

二、UIImageView

UIImageViewUIView 的子类,专门用于显示 UIImage 对象,支持静态图片和帧动画。

特点

  • 高效渲染:直接将图片设置到 layer.contents,不经过 drawRect:,渲染性能高。
  • 帧动画支持:通过 animationImages 设置一组图片,实现序列帧动画。
  • 内容模式:通过 contentMode 控制图片在视图内的缩放与对齐。
  • 默认不响应交互userInteractionEnabled 默认为 NO

用法示例

Objective-C

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 110, 300, 200)];
imageView.image = [UIImage imageNamed:@"example.png"];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.clipsToBounds = YES; // 配合 aspectFill 使用,防止图片溢出
[self.view addSubview:imageView];

// 帧动画
NSArray *frames = @[
    [UIImage imageNamed:@"frame1.png"],
    [UIImage imageNamed:@"frame2.png"],
    [UIImage imageNamed:@"frame3.png"]
];
imageView.animationImages = frames;
imageView.animationDuration = 1.0;  // 一轮动画时长(秒)
imageView.animationRepeatCount = 0; // 0 表示无限循环
[imageView startAnimating];

Swift

let imageView = UIImageView(frame: CGRect(x: 10, y: 110, width: 300, height: 200))
imageView.image = UIImage(named: "example.png")
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true // 配合 aspectFill 使用,防止图片溢出
view.addSubview(imageView)

// 帧动画
imageView.animationImages = [
    UIImage(named: "frame1.png")!,
    UIImage(named: "frame2.png")!,
    UIImage(named: "frame3.png")!
]
imageView.animationDuration = 1.0
imageView.animationRepeatCount = 0 // 无限循环
imageView.startAnimating()

底层实现

  • UIImageView 继承自 UIView,底层基于 Core Animation
  • 设置 image 属性时,将 UIImage.CGImage 赋值给自身 layer.contents,由 Core Animation 直接渲染——不经过 drawRect:,这是其高效的关键。
  • contentMode 映射到 layer.contentsGravity,决定图片在图层内的缩放与对齐。
  • 帧动画(animationImages)由 UIImageView 内部定时切换 layer.contents 实现,不是通过 CABasicAnimation
  • 不存在所谓的 CAContentLayer 类——UIImageView 使用的就是普通 CALayer(或其私有子类),图片直接放在 layer.contents 上。

三、UIViewContentMode

UIViewContentModeUIViewcontentMode 属性的枚举类型,用于控制视图自身内容(如 layer.contentsdrawRect: 绘制的内容)在视图 bounds 内的缩放与对齐方式。

概念澄清contentMode 控制的是视图自己的内容,不是子视图的位置和缩放。子视图的布局由 autoresizingMask 或 Auto Layout 约束决定。原文"控制子视图在其父视图中的位置和缩放方式"是不准确的。

全部枚举值

枚举值 说明
scaleToFill 拉伸内容填满视图,不保持宽高比(可能变形)
scaleAspectFit 按比例缩放,完整显示内容(可能留空白)
scaleAspectFill 按比例缩放,填满视图(可能裁剪,需配合 clipsToBounds = YES
redraw 视图 bounds 变化时调用 setNeedsDisplay 重绘
center 不缩放,居中对齐
top / bottom / left / right 不缩放,分别靠上/下/左/右对齐
topLeft / topRight / bottomLeft / bottomRight 不缩放,四角对齐

最常用的是前三种缩放模式和 center

用法示例

Objective-C

// scaleToFill:拉伸填充,可能变形
UIImageView *iv1 = [[UIImageView alloc] initWithFrame:CGRectMake(10, 320, 80, 80)];
iv1.image = [UIImage imageNamed:@"example.png"];
iv1.contentMode = UIViewContentModeScaleToFill;
iv1.clipsToBounds = YES;
[self.view addSubview:iv1];

// scaleAspectFit:按比例适应,完整显示(可能留黑边)
UIImageView *iv2 = [[UIImageView alloc] initWithFrame:CGRectMake(100, 320, 80, 80)];
iv2.image = [UIImage imageNamed:@"example.png"];
iv2.contentMode = UIViewContentModeScaleAspectFit;
[self.view addSubview:iv2];

// scaleAspectFill:按比例填充,可能裁剪
UIImageView *iv3 = [[UIImageView alloc] initWithFrame:CGRectMake(190, 320, 80, 80)];
iv3.image = [UIImage imageNamed:@"example.png"];
iv3.contentMode = UIViewContentModeScaleAspectFill;
iv3.clipsToBounds = YES; // 必须开启,否则溢出部分仍会显示
[self.view addSubview:iv3];

// center:不缩放,居中
UIImageView *iv4 = [[UIImageView alloc] initWithFrame:CGRectMake(280, 320, 80, 80)];
iv4.image = [UIImage imageNamed:@"example.png"];
iv4.contentMode = UIViewContentModeCenter;
[self.view addSubview:iv4];

Swift

// scaleToFill
let iv1 = UIImageView(frame: CGRect(x: 10, y: 320, width: 80, height: 80))
iv1.image = UIImage(named: "example.png")
iv1.contentMode = .scaleToFill
iv1.clipsToBounds = true
view.addSubview(iv1)

// scaleAspectFit
let iv2 = UIImageView(frame: CGRect(x: 100, y: 320, width: 80, height: 80))
iv2.image = UIImage(named: "example.png")
iv2.contentMode = .scaleAspectFit
view.addSubview(iv2)

// scaleAspectFill
let iv3 = UIImageView(frame: CGRect(x: 190, y: 320, width: 80, height: 80))
iv3.image = UIImage(named: "example.png")
iv3.contentMode = .scaleAspectFill
iv3.clipsToBounds = true // 必须开启裁剪
view.addSubview(iv3)

// center
let iv4 = UIImageView(frame: CGRect(x: 280, y: 320, width: 80, height: 80))
iv4.image = UIImage(named: "example.png")
iv4.contentMode = .center
view.addSubview(iv4)

三种缩放模式的直观对比

scaleToFill          scaleAspectFit      scaleAspectFill
┌──────────┐         ┌──────────┐        ┌──────────┐
│██████████│         │  ██████  │        │██████████│
│██████████│         │  ██████  │        │██████████│
│██████████│         │  ██████  │        │██████████│
└──────────┘         └──────────┘        └──────────┘
  拉伸变形              留空白(完整)         裁剪(填满)

四、总结对比

维度 UIImage UIImageView UIViewContentMode
角色 图像数据封装 图像展示视图 内容对齐/缩放枚举
职责 加载、处理图像数据 渲染图像到屏幕 控制内容在视图内的布局
是否显示 不涉及,是配置项
可变/不可变 不可变 可变(可换图) 枚举值
底层依赖 Core Graphics / Core Image Core Animation 映射到 layer.contentsGravity

UIImage

  • 优点:支持多种格式和加载方式,提供图像处理能力。
  • 局限:仅负责数据,不负责显示。
  • 注意imageNamed: 带缓存,适合小图;大图用 imageWithContentsOfFile:

UIImageView

  • 优点:高效渲染(直接走 layer.contents),支持帧动画。
  • 局限:仅用于展示,默认不响应交互。
  • 注意scaleAspectFill 必须配合 clipsToBounds = YES,否则图片会溢出视图边界。

UIViewContentMode

  • 优点:灵活控制内容的缩放与对齐,无需手动计算 frame。
  • 局限:仅控制视图自身内容,不影响子视图布局。
  • 注意:控制的是视图自身内容,不是子视图。
posted @ 2015-07-15 02:19  Mr.陳  阅读(4677)  评论(0)    收藏  举报