在 macOS / iOS 中,设置图片尺寸本质上是对 NSImage 或 CGImage 做缩放/重绘。不能直接修改原始 NSImage 的像素数据大小,而是需要生成一个新的图像。
1、使用 NSImage 重绘
import AppKit
func resizeImage(_ image: NSImage, to newSize: CGSize) -> NSImage? {
let newImage = NSImage(size: newSize)
newImage.lockFocus()
defer { newImage.unlockFocus() }
// 拉伸或压缩绘制原图
image.draw(
in: NSRect(origin: .zero, size: newSize),
from: NSRect(origin: .zero, size: image.size),
operation: .copy,
fraction: 1.0
)
return newImage
}
newSize 是目标尺寸(像素单位取决于图像分辨率)。
.lockFocus() / .unlockFocus() 用于在内存中绘制新的图像。
fraction: 1.0 表示完全不透明绘制。
这种方法适用于macOS中的NSImage复制一个新的NSImage,可以用于后续压缩或保存。
2、使用 CGImage / Core Graphics 缩放
import CoreGraphics
func resizeCGImage(_ cgImage: CGImage, to newSize: CGSize) -> CGImage? {
let colorSpace = cgImage.colorSpace ?? CGColorSpaceCreateDeviceRGB()
guard let context = CGContext(
data: nil,
width: Int(newSize.width),
height: Int(newSize.height),
bitsPerComponent: cgImage.bitsPerComponent,
bytesPerRow: 0,
space: colorSpace,
bitmapInfo: cgImage.bitmapInfo.rawValue
) else { return nil }
// 绘制缩放
context.interpolationQuality = .high
context.draw(cgImage, in: CGRect(origin: .zero, size: newSize))
return context.makeImage()
}
这种方法适用于macOS中的CGImage复制一个新的CGImage。
可以设置 interpolationQuality 来控制缩放质量(.low / .medium / .high / .none)。
3、NSBitmapImageRep 缩放(适合保存位图)
func resizeWithBitmap(_ image: NSImage, newSize: CGSize) -> Data? {
guard let tiffData = image.tiffRepresentation,
let bitmap = NSBitmapImageRep(data: tiffData) else { return nil }
let resizedBitmap = NSBitmapImageRep(
bitmapDataPlanes: nil,
pixelsWide: Int(newSize.width),
pixelsHigh: Int(newSize.height),
bitsPerSample: bitmap.bitsPerSample,
samplesPerPixel: bitmap.samplesPerPixel,
hasAlpha: bitmap.hasAlpha,
isPlanar: bitmap.isPlanar,
colorSpaceName: bitmap.colorSpaceName,
bytesPerRow: 0,
bitsPerPixel: 0
)
resizedBitmap?.size = newSize
// 重新绘制
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = NSGraphicsContext(bitmapImageRep: resizedBitmap!)
image.draw(in: NSRect(origin: .zero, size: newSize))
NSGraphicsContext.restoreGraphicsState()
return resizedBitmap?.representation(using: .jpeg, properties: [.compressionFactor: 1.0])
}
可以直接生成 JPEG / PNG Data。
适合直接压缩+输出。
注意事项
1、比例缩放 vs 指定大小
如果只指定 newSize,图片可能会被拉伸/压缩失真。
如果想保持比例,可以先计算缩放比例:
let aspectRatio = image.size.width / image.size.height
let targetWidth: CGFloat = 800
let targetHeight = targetWidth / aspectRatio
2、高分屏/Retina
NSImage 有 size 属性和像素分辨率两套数据,缩放时要注意 pixelsWide / pixelsHigh。
3、缩放后压缩
缩放后的图片再走 CGImageDestination 压缩,可以更好控制文件大小。