iOS开发基础3-图像处理详解:UIImage、UIImageView 与 UIViewContentMode
iOS 图像处理详解:UIImage、UIImageView 与 UIViewContentMode
在 iOS 开发中,UIImage 负责图像数据的加载与处理,UIImageView 负责图像的展示,UIViewContentMode 则控制内容在视图内的缩放与对齐方式。三者配合,覆盖了图像从加载到渲染的完整链路。
一、UIImage
UIImage 是图像数据的高层封装,管理图像的像素数据,并提供加载、缩放、裁剪等操作接口。
特点
- 支持多种图片格式:PNG、JPEG、HEIC、GIF(静态首帧)等。
- 多种加载方式:从 bundle 资源、文件路径、
NSData、CGImage/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提供面向对象的高层接口。 UIImage有scale属性,表示图像的缩放因子(@1x/@2x/@3x),与屏幕scale配合决定实际渲染像素数。UIImage本身不负责显示,只负责持有和处理图像数据。
二、UIImageView
UIImageView 是 UIView 的子类,专门用于显示 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
UIViewContentMode 是 UIView 的 contentMode 属性的枚举类型,用于控制视图自身内容(如 layer.contents 或 drawRect: 绘制的内容)在视图 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。
- 局限:仅控制视图自身内容,不影响子视图布局。
- 注意:控制的是视图自身内容,不是子视图。
将来的你会感谢今天如此努力的你!
版权声明:本文为博主原创文章,未经博主允许不得转载。

浙公网安备 33010602011771号