Article / 2026/05/29
UIImage 完整指南
1. 内存管理:大图像使用 init(contentsOfFile:) 避免缓存 2. 性能优化:在后台队列进行图像处理操作 3. 格式选择:PNG 用于透明图像,JPEG 用于照片 4. 缓存策略:合理使用图像缓存避免内存压力 5....
🚀 UIImage 完整指南
概述
UIImage 是 UIKit 框架中用于表示图像数据的核心类,提供了图像的创建、处理、渲染和操作等功能。
类层次结构
NSObject
└── UIImage
初始化方法
1. 基础初始化
// 从应用包中加载图像
init?(named name: String)
init?(named name: String, in bundle: Bundle?, compatibleWith traitCollection: UITraitCollection?)
// 从文件路径创建
init?(contentsOfFile path: String)
// 从数据创建
init?(data: Data)
init?(data: Data, scale: CGFloat)
// 从 CGImage 创建
init(cgImage: CGImage)
init(cgImage: CGImage, scale: CGFloat, orientation: UIImage.Orientation)
// 从 CIImage 创建
init(ciImage: CIImage)
init(ciImage: CIImage, scale: CGFloat, orientation: UIImage.Orientation)
2. 系统图标 (iOS 13+)
// SF Symbols
init?(systemName: String)
init?(systemName: String, withConfiguration configuration: UIImage.Configuration?)
// 示例
let heartIcon = UIImage(systemName: "heart.fill")
let starIcon = UIImage(systemName: "star", withConfiguration: UIImage.SymbolConfiguration(pointSize: 24))
3. 空白和纯色图像
// 创建空白图像
extension UIImage {
static func blank(size: CGSize, color: UIColor = .clear) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0)
defer { UIGraphicsEndImageContext() }
color.setFill()
UIRectFill(CGRect(origin: .zero, size: size))
return UIGraphicsGetImageFromCurrentImageContext() ?? UIImage()
}
}
核心属性
1. 基本属性
// 图像尺寸 (点)
var size: CGSize { get }
// 缩放比例
var scale: CGFloat { get }
// 图像方向
var orientation: UIImage.Orientation { get }
// 图像配置 (iOS 13+)
var configuration: UIImage.Configuration? { get }
// 持续时间 (动画图像)
var duration: TimeInterval { get }
// 图像数组 (动画图像)
var images: [UIImage]? { get }
2. 底层图像数据
// CGImage 表示
var cgImage: CGImage? { get }
// CIImage 表示
var ciImage: CIImage? { get }
// 图像资产信息 (iOS 9+)
var imageAsset: UIImageAsset? { get }
// 图像渲染模式
var imageRendererFormat: UIGraphicsImageRendererFormat { get }
3. 渲染和显示属性
// 渲染模式
var renderingMode: UIImage.RenderingMode { get }
// 调整大小模式 (iOS 6+)
var resizingMode: UIImage.ResizingMode { get }
// 边缘插入 (用于可拉伸图像)
var capInsets: UIEdgeInsets { get }
// 对齐矩形插入
var alignmentRectInsets: UIEdgeInsets { get }
// 基线偏移 (iOS 14+)
var baselineOffsetFromBottom: CGFloat { get }
// 是否有 Alpha 通道
var hasAlpha: Bool { get }
图像方向
enum UIImage.Orientation : Int {
case up // 正常方向
case down // 180度旋转
case left // 逆时针90度
case right // 顺时针90度
case upMirrored // 水平翻转
case downMirrored // 垂直翻转 + 水平翻转
case leftMirrored // 逆时针90度 + 垂直翻转
case rightMirrored // 顺时针90度 + 垂直翻转
}
渲染模式
enum UIImage.RenderingMode : Int {
case automatic // 自动模式
case alwaysOriginal // 始终显示原始图像
case alwaysTemplate // 始终作为模板图像 (单色)
}
// 设置渲染模式
let templateImage = originalImage.withRenderingMode(.alwaysTemplate)
调整大小模式
enum UIImage.ResizingMode : Int {
case tile // 平铺
case stretch // 拉伸
}
// 创建可拉伸图像
let stretchableImage = image.stretchableImage(withLeftCapWidth: 10, topCapHeight: 10)
let resizableImage = image.resizableImage(withCapInsets: UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10))
图像配置 (iOS 13+)
1. 符号配置
// 符号配置
class UIImage.SymbolConfiguration {
// 点大小
init(pointSize: CGFloat)
init(pointSize: CGFloat, weight: UIImage.SymbolWeight)
init(pointSize: CGFloat, weight: UIImage.SymbolWeight, scale: UIImage.SymbolScale)
// 文本样式
init(textStyle: UIFont.TextStyle)
init(textStyle: UIFont.TextStyle, scale: UIImage.SymbolScale)
// 权重和缩放
init(weight: UIImage.SymbolWeight)
init(scale: UIImage.SymbolScale)
// 字体
init(font: UIFont)
init(font: UIFont, scale: UIImage.SymbolScale)
}
// 符号权重
enum UIImage.SymbolWeight {
case ultraLight, thin, light, regular, medium, semibold, bold, heavy, black
}
// 符号缩放
enum UIImage.SymbolScale {
case small, medium, large
}
2. 配置示例
// 不同配置的系统图标
let smallIcon = UIImage(systemName: "heart.fill", withConfiguration: UIImage.SymbolConfiguration(scale: .small))
let largeIcon = UIImage(systemName: "heart.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 30, weight: .bold))
let textStyleIcon = UIImage(systemName: "star", withConfiguration: UIImage.SymbolConfiguration(textStyle: .title1))
图像处理方法
1. 图像变换
// 应用配置
func withConfiguration(_ configuration: UIImage.Configuration) -> UIImage
// 渲染模式
func withRenderingMode(_ renderingMode: UIImage.RenderingMode) -> UIImage
// 色调颜色 (iOS 13+)
func withTintColor(_ color: UIColor) -> UIImage
func withTintColor(_ color: UIColor, renderingMode: UIImage.RenderingMode) -> UIImage
// 水平翻转
func withHorizontallyFlippedOrientation() -> UIImage
// 对齐矩形插入
func withAlignmentRectInsets(_ alignmentInsets: UIEdgeInsets) -> UIImage
// 基线偏移 (iOS 14+)
func withBaselineOffset(fromBottom baselineOffset: CGFloat) -> UIImage
2. 图像调整大小
// 可拉伸图像
func stretchableImage(withLeftCapWidth leftCapWidth: Int, topCapHeight topCapHeight: Int) -> UIImage
func resizableImage(withCapInsets capInsets: UIEdgeInsets) -> UIImage
func resizableImage(withCapInsets capInsets: UIEdgeInsets, resizingMode: UIImage.ResizingMode) -> UIImage
// 自定义调整大小
extension UIImage {
func resized(to size: CGSize) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: size))
}
}
func aspectFitResized(to size: CGSize) -> UIImage {
let aspectRatio = self.size.width / self.size.height
let targetAspectRatio = size.width / size.height
var newSize = size
if aspectRatio > targetAspectRatio {
newSize.height = size.width / aspectRatio
} else {
newSize.width = size.height * aspectRatio
}
return resized(to: newSize)
}
}
3. 图像绘制
// 在指定矩形中绘制
func draw(in rect: CGRect)
func draw(in rect: CGRect, blendMode: CGBlendMode, alpha: CGFloat)
// 在指定点绘制
func draw(at point: CGPoint)
func draw(at point: CGPoint, blendMode: CGBlendMode, alpha: CGFloat)
// 绘制平铺图案
func drawAsPattern(in rect: CGRect)
动画图像
1. 创建动画图像
// 从图像数组创建
class func animatedImage(with images: [UIImage], duration: TimeInterval) -> UIImage?
// 从 GIF 数据创建
extension UIImage {
static func animatedImageWithGIF(data: Data) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data, nil) else { return nil }
return animatedImageWithSource(source)
}
private static func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images: [UIImage] = []
var duration: TimeInterval = 0
for i in 0..<count {
guard let cgImage = CGImageSourceCreateImageAtIndex(source, i, nil) else { continue }
let image = UIImage(cgImage: cgImage)
images.append(image)
// 获取帧持续时间
let delayTime = getFrameDelay(from: source, at: i)
duration += delayTime
}
return UIImage.animatedImage(with: images, duration: duration)
}
private static func getFrameDelay(from source: CGImageSource, at index: Int) -> TimeInterval {
guard let properties = CGImageSourceCopyPropertiesAtIndex(source, index, nil) as? [String: Any] else { return 0.1 }
if let gifProperties = properties[kCGImagePropertyGIFDictionary as String] as? [String: Any] {
if let delayTime = gifProperties[kCGImagePropertyGIFDelayTime as String] as? NSNumber {
return delayTime.doubleValue
}
}
return 0.1
}
}
2. 动画属性
// 动画帧数组
var images: [UIImage]? { get }
// 动画总时长
var duration: TimeInterval { get }
// 检查是否为动画图像
var isAnimated: Bool {
return images != nil && !images!.isEmpty
}
图像资产 (iOS 9+)
// 图像资产
class UIImageAsset {
// 注册图像
func register(_ image: UIImage, with traitCollection: UITraitCollection)
// 获取图像
func image(with traitCollection: UITraitCollection) -> UIImage
// 注销图像
func unregister(imageWith traitCollection: UITraitCollection)
}
// 使用示例
let asset = UIImageAsset()
asset.register(lightImage, with: UITraitCollection(userInterfaceStyle: .light))
asset.register(darkImage, with: UITraitCollection(userInterfaceStyle: .dark))
图像缓存
1. 系统缓存
// UIImage.init(named:) 自动缓存
// 大图像建议使用 init(contentsOfFile:) 避免缓存
// 检查图像是否被缓存
extension UIImage {
static func isCached(named name: String) -> Bool {
return UIImage(named: name) != nil
}
}
2. 自定义缓存
class ImageCache {
private let cache = NSCache<NSString, UIImage>()
func setImage(_ image: UIImage, forKey key: String) {
cache.setObject(image, forKey: key as NSString)
}
func image(forKey key: String) -> UIImage? {
return cache.object(forKey: key as NSString)
}
func removeImage(forKey key: String) {
cache.removeObject(forKey: key as NSString)
}
func clearCache() {
cache.removeAllObjects()
}
}
图像压缩和格式转换
1. JPEG 压缩
extension UIImage {
func jpegData(compressionQuality: CGFloat = 0.8) -> Data? {
return self.jpegData(compressionQuality: compressionQuality)
}
func compressedJPEG(quality: CGFloat = 0.8) -> UIImage? {
guard let data = jpegData(compressionQuality: quality) else { return nil }
return UIImage(data: data)
}
}
2. PNG 数据
extension UIImage {
func pngData() -> Data? {
return self.pngData()
}
}
3. WebP 支持 (需要第三方库)
// 需要集成 WebP 库
extension UIImage {
static func fromWebP(data: Data) -> UIImage? {
// WebP 解码实现
return nil
}
func webPData() -> Data? {
// WebP 编码实现
return nil
}
}
图像效果和滤镜
1. Core Image 滤镜
extension UIImage {
func applyFilter(name: String, parameters: [String: Any]? = nil) -> UIImage? {
guard let ciImage = CIImage(image: self) else { return nil }
guard let filter = CIFilter(name: name) else { return nil }
filter.setValue(ciImage, forKey: kCIInputImageKey)
parameters?.forEach { key, value in
filter.setValue(value, forKey: key)
}
guard let outputImage = filter.outputImage else { return nil }
let context = CIContext()
guard let cgImage = context.createCGImage(outputImage, from: outputImage.extent) else { return nil }
return UIImage(cgImage: cgImage)
}
// 常用滤镜
func blurred(radius: CGFloat = 10) -> UIImage? {
return applyFilter(name: "CIGaussianBlur", parameters: [kCIInputRadiusKey: radius])
}
func sepia() -> UIImage? {
return applyFilter(name: "CISepiaTone", parameters: [kCIInputIntensityKey: 0.8])
}
func noir() -> UIImage? {
return applyFilter(name: "CIPhotoEffectNoir")
}
}
2. 颜色调整
extension UIImage {
func tinted(with color: UIColor) -> UIImage? {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { context in
color.set()
self.draw(in: CGRect(origin: .zero, size: size))
context.cgContext.setBlendMode(.sourceAtop)
context.fill(CGRect(origin: .zero, size: size))
}
}
func grayscale() -> UIImage? {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { _ in
let rect = CGRect(origin: .zero, size: size)
self.draw(in: rect)
if let cgImage = UIGraphicsGetImageFromCurrentImageContext()?.cgImage {
let colorSpace = CGColorSpaceCreateDeviceGray()
let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.none.rawValue)
context?.draw(cgImage, in: rect)
if let grayImage = context?.makeImage() {
return UIImage(cgImage: grayImage)
}
}
return self
}
}
}
图像元数据
1. EXIF 信息
extension UIImage {
var exifData: [String: Any]? {
guard let cgImage = self.cgImage,
let data = UIImage.jpegData(self)(compressionQuality: 1.0),
let source = CGImageSourceCreateWithData(data, nil),
let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [String: Any] else {
return nil
}
return properties
}
var gpsInfo: [String: Any]? {
return exifData?[kCGImagePropertyGPSDictionary as String] as? [String: Any]
}
var cameraInfo: [String: Any]? {
return exifData?[kCGImagePropertyExifDictionary as String] as? [String: Any]
}
}
2. 图像信息
extension UIImage {
var pixelSize: CGSize {
guard let cgImage = self.cgImage else { return .zero }
return CGSize(width: cgImage.width, height: cgImage.height)
}
var aspectRatio: CGFloat {
return size.width / size.height
}
var isLandscape: Bool {
return size.width > size.height
}
var isPortrait: Bool {
return size.height > size.width
}
var isSquare: Bool {
return size.width == size.height
}
}
性能优化
1. 图像预加载
class ImagePreloader {
private var loadedImages: [String: UIImage] = [:]
func preloadImages(names: [String], completion: @escaping () -> Void) {
let group = DispatchGroup()
names.forEach { name in
group.enter()
DispatchQueue.global(qos: .utility).async {
if let image = UIImage(named: name) {
DispatchQueue.main.async {
self.loadedImages[name] = image
group.leave()
}
} else {
group.leave()
}
}
}
group.notify(queue: .main) {
completion()
}
}
func image(named name: String) -> UIImage? {
return loadedImages[name]
}
}
2. 内存优化
extension UIImage {
// 解压图像以避免主线程解压
func decompressed() -> UIImage? {
guard let cgImage = self.cgImage else { return nil }
let colorSpace = CGColorSpaceCreateDeviceRGB()
let context = CGContext(
data: nil,
width: cgImage.width,
height: cgImage.height,
bitsPerComponent: 8,
bytesPerRow: cgImage.width * 4,
space: colorSpace,
bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue
)
context?.draw(cgImage, in: CGRect(origin: .zero, size: CGSize(width: cgImage.width, height: cgImage.height)))
guard let decompressedCGImage = context?.makeImage() else { return nil }
return UIImage(cgImage: decompressedCGImage, scale: scale, orientation: imageOrientation)
}
// 生成缩略图
func thumbnail(size: CGSize) -> UIImage? {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: size))
}
}
}
错误处理和调试
1. 图像验证
extension UIImage {
var isValid: Bool {
return self.size.width > 0 && self.size.height > 0
}
static func validate(named name: String) -> ValidationResult {
guard let image = UIImage(named: name) else {
return .notFound
}
if !image.isValid {
return .invalid
}
return .valid(image)
}
}
enum ValidationResult {
case valid(UIImage)
case notFound
case invalid
}
2. 调试工具
extension UIImage {
func debugInfo() -> String {
var info = "UIImage Debug Info:\n"
info += "Size: \(size)\n"
info += "Scale: \(scale)\n"
info += "Orientation: \(orientation.rawValue)\n"
info += "Has Alpha: \(hasAlpha)\n"
info += "Rendering Mode: \(renderingMode.rawValue)\n"
if let cgImage = self.cgImage {
info += "Pixel Size: \(cgImage.width)x\(cgImage.height)\n"
info += "Bits Per Component: \(cgImage.bitsPerComponent)\n"
info += "Bits Per Pixel: \(cgImage.bitsPerPixel)\n"
info += "Color Space: \(cgImage.colorSpace?.name ?? "Unknown")\n"
}
return info
}
}
常用扩展方法
1. 图像生成
extension UIImage {
// 从颜色生成图像
static func from(color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { context in
color.setFill()
context.fill(CGRect(origin: .zero, size: size))
}
}
// 从渐变生成图像
static func gradient(colors: [UIColor], size: CGSize, direction: GradientDirection = .topToBottom) -> UIImage {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { context in
let cgColors = colors.map { $0.cgColor }
let colorSpace = CGColorSpaceCreateDeviceRGB()
guard let gradient = CGGradient(colorsSpace: colorSpace, colors: cgColors as CFArray, locations: nil) else { return }
let startPoint: CGPoint
let endPoint: CGPoint
switch direction {
case .topToBottom:
startPoint = CGPoint(x: 0, y: 0)
endPoint = CGPoint(x: 0, y: size.height)
case .leftToRight:
startPoint = CGPoint(x: 0, y: 0)
endPoint = CGPoint(x: size.width, y: 0)
case .topLeftToBottomRight:
startPoint = CGPoint(x: 0, y: 0)
endPoint = CGPoint(x: size.width, y: size.height)
case .topRightToBottomLeft:
startPoint = CGPoint(x: size.width, y: 0)
endPoint = CGPoint(x: 0, y: size.height)
}
context.cgContext.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: [])
}
}
}
enum GradientDirection {
case topToBottom
case leftToRight
case topLeftToBottomRight
case topRightToBottomLeft
}
2. 图像处理
extension UIImage {
// 圆形裁剪
func circularImage() -> UIImage? {
let minSize = min(size.width, size.height)
let renderer = UIGraphicsImageRenderer(size: CGSize(width: minSize, height: minSize))
return renderer.image { _ in
let rect = CGRect(origin: .zero, size: CGSize(width: minSize, height: minSize))
UIBezierPath(ovalIn: rect).addClip()
let drawRect = CGRect(
x: (minSize - size.width) / 2,
y: (minSize - size.height) / 2,
width: size.width,
height: size.height
)
self.draw(in: drawRect)
}
}
// 圆角裁剪
func roundedCorners(radius: CGFloat) -> UIImage? {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { _ in
let rect = CGRect(origin: .zero, size: size)
UIBezierPath(roundedRect: rect, cornerRadius: radius).addClip()
self.draw(in: rect)
}
}
// 添加边框
func withBorder(width: CGFloat, color: UIColor) -> UIImage? {
let renderer = UIGraphicsImageRenderer(size: size)
return renderer.image { context in
let rect = CGRect(origin: .zero, size: size)
self.draw(in: rect)
context.cgContext.setStrokeColor(color.cgColor)
context.cgContext.setLineWidth(width)
context.cgContext.stroke(rect.insetBy(dx: width/2, dy: width/2))
}
}
}
最佳实践
- 内存管理:大图像使用
init(contentsOfFile:)避免缓存 - 性能优化:在后台队列进行图像处理操作
- 格式选择:PNG 用于透明图像,JPEG 用于照片
- 缓存策略:合理使用图像缓存避免内存压力
- 资源管理:及时释放不需要的图像资源
- 适配性:使用资产目录支持不同设备和主题
- 压缩优化:根据使用场景选择合适的压缩质量
总结
UIImage 是 iOS 开发中处理图像的核心类,提供了丰富的功能用于图像的创建、处理和显示。iOS 13+ 引入的系统图标和配置系统进一步增强了图像的灵活性。合理使用 UIImage 的各种功能可以创建出高质量的用户界面和良好的用户体验。
Giscus 未启用:请在
src/site.config.ts中配置 repoId 与 categoryId。