Работа с изображениями на Swift: resize и crop для iOS и macOS
Обработка изображений — одна из самых частых задач при разработке мобильных и десктопных приложений. На платформах Apple (iOS и macOS) для изменения размера (resize) и обрезки (crop) изображений можно использовать как встроенные фреймворки (UIKit для iOS, AppKit для macOS), так и мощный низкоуровневый Core Graphics. В этой статье мы разберём универсальные методы, которые работают на обеих платформах, и приведём готовые примеры кода на Swift.
Основные подходы к обработке изображений
Существует три основных способа изменить размер или обрезать изображение в Swift:
- UIKit (iOS/tvOS) — использует класс
UIImageи его методыdraw(in:)илиUIGraphicsImageRenderer. - AppKit (macOS) — использует класс
NSImageи работу сNSGraphicsContext. - Core Graphics (Quartz 2D) — кроссплатформенный низкоуровневый C-фреймворк, который работает как на iOS, так и на macOS. Основан на
CGImageи графических контекстах.
Для универсальности мы будем использовать Core Graphics, так как он позволяет писать код, который легко адаптируется под обе платформы. Однако для iOS-проектов часто удобнее применять UIGraphicsImageRenderer — он проще и быстрее.
Изменение размера изображения (Resize)
Функция resize принимает исходное изображение (UIImage или NSImage) и новый размер (CGSize). Алгоритм прост: создаётся графический контекст нужного размера, в него рисуется исходное изображение, затем из контекста извлекается новое изображение.
Пример для iOS (UIKit)
import UIKit
extension UIImage {
func resized(to newSize: CGSize) -> UIImage? {
let renderer = UIGraphicsImageRenderer(size: newSize)
return renderer.image { _ in
self.draw(in: CGRect(origin: .zero, size: newSize))
}
}
}
// Использование:
if let image = UIImage(named: "example"),
let resized = image.resized(to: CGSize(width: 300, height: 200)) {
// используем resized
}
Пример для macOS (AppKit + Core Graphics)
import AppKit
extension NSImage {
func resized(to newSize: NSSize) -> NSImage? {
guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return nil }
let bitmap = NSBitmapImageRep(
bitmapDataPlanes: nil,
pixelsWide: Int(newSize.width),
pixelsHigh: Int(newSize.height),
bitsPerSample: 8,
samplesPerPixel: 4,
hasAlpha: true,
isPlanar: false,
colorSpaceName: .deviceRGB,
bitmapFormat: .alphaFirst,
bytesPerRow: 0,
bitsPerPixel: 0
)
guard let context = NSGraphicsContext(bitmapImageRep: bitmap!) else { return nil }
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.current = context
let rect = NSRect(origin: .zero, size: newSize)
self.draw(in: rect, from: .zero, operation: .copy, fraction: 1.0)
NSGraphicsContext.restoreGraphicsState()
let newImage = NSImage(size: newSize)
newImage.addRepresentation(bitmap!)
return newImage
}
}
Обрезка изображения (Crop)
Обрезка (crop) — это выделение прямоугольной области из исходного изображения. В Core Graphics для этого используется функция cropping(to:) у CGImage. Важно помнить, что координаты в Core Graphics начинаются с левого нижнего угла, поэтому для iOS (где ось Y направлена вниз) может потребоваться преобразование координат.
Универсальная функция crop для iOS и macOS
import CoreGraphics
extension CGImage {
func cropped(to rect: CGRect) -> CGImage? {
// Убедимся, что rect находится внутри изображения
let x = max(0, min(CGFloat(self.width) - 1, rect.origin.x))
let y = max(0, min(CGFloat(self.height) - 1, rect.origin.y))
let width = min(rect.width, CGFloat(self.width) - x)
let height = min(rect.height, CGFloat(self.height) - y)
let safeRect = CGRect(x: x, y: y, width: width, height: height)
return self.cropping(to: safeRect)
}
}
// Использование для iOS:
extension UIImage {
func cropped(to rect: CGRect) -> UIImage? {
guard let cgImage = self.cgImage else { return nil }
// Для iOS нужно перевернуть Y-координату
let flippedRect = CGRect(
x: rect.origin.x,
y: CGFloat(cgImage.height) - rect.origin.y - rect.height