Работа с изображениями: resize, crop на Swift (iOS/macOS)
Обработка изображений — одна из ключевых задач при разработке приложений для iOS и macOS. Будь то загрузка аватара пользователя, подготовка превью фотографий или работа с графическим контентом, вам почти наверняка понадобятся операции изменения размера (resize) и обрезки (crop). В этой статье мы разберем, как выполнять эти задачи на Swift, используя встроенные фреймворки UIKit (для iOS) и AppKit (для macOS), а также универсальный Core Graphics.
Основные подходы
В экосистеме Apple существует несколько способов манипуляции с изображениями. Мы рассмотрим два самых популярных и производительных:
- UIKit / AppKit + Core Graphics — низкоуровневый, но очень гибкий подход. Позволяет точно контролировать качество и размер результата.
- Image I/O — фреймворк для работы с метаданными и кодированием/декодированием изображений. Отлично подходит для задач, где важна производительность при работе с большими файлами.
В этой статье мы сосредоточимся на первом методе, так как он наиболее универсален и не требует дополнительных зависимостей.
Изменение размера изображения (Resize)
Функция изменения размера принимает исходное изображение и целевой размер, а возвращает новое изображение с заданными параметрами. Важно сохранять пропорции, чтобы избежать искажений.
Пример для iOS (UIKit)
import UIKit
extension UIImage {
func resized(to targetSize: CGSize) -> UIImage? {
// Определяем масштаб, чтобы вписаться в targetSize, сохраняя пропорции
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
let scale = min(widthRatio, heightRatio)
// Новый размер с сохранением пропорций
let newSize = CGSize(width: size.width * scale, height: size.height * scale)
// Создаем графический контекст с новым размером
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
draw(in: CGRect(origin: .zero, size: newSize))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage
}
}
// Использование:
if let image = UIImage(named: "example"),
let resized = image.resized(to: CGSize(width: 300, height: 200)) {
// Используем resized
}
Пример для macOS (AppKit)
import AppKit
extension NSImage {
func resized(to targetSize: NSSize) -> NSImage? {
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
let scale = min(widthRatio, heightRatio)
let newSize = NSSize(width: size.width * scale, height: size.height * scale)
let newImage = NSImage(size: newSize)
newImage.lockFocus()
draw(in: NSRect(origin: .zero, size: newSize),
from: NSRect(origin: .zero, size: size),
operation: .copy,
fraction: 1.0)
newImage.unlockFocus()
return newImage
}
}
Важно: Для macOS функцияUIGraphicsBeginImageContextWithOptionsнедоступна. ИспользуетсяlockFocus()/unlockFocus()или Core Graphics.
Обрезка изображения (Crop)
Обрезка позволяет выделить прямоугольную область из исходного изображения. Чаще всего используется для создания квадратных аватаров или удаления лишних краев.
Универсальный метод с использованием Core Graphics
import CoreGraphics
extension UIImage {
func cropped(to rect: CGRect) -> UIImage? {
guard let cgImage = self.cgImage else { return nil }
// Учитываем scale экрана (для Retina дисплеев)
let scaledRect = CGRect(x: rect.origin.x * scale,
y: rect.origin.y * scale,
width: rect.width * scale,
height: rect.height * scale)
guard let croppedCGImage = cgImage.cropping(to: scaledRect) else { return nil }
return UIImage(cgImage: croppedCGImage, scale: scale, orientation: imageOrientation)
}
}
// Пример: вырезаем квадрат 200x200 из центра
if let image = UIImage(named: "photo") {
let side = min(image.size.width, image.size.height)
let cropRect = CGRect(x: (image.size.width - side) / 2,
y: (image.size.height - side) / 2,
width: side, height: side)
let croppedImage = image.cropped(to: cropRect)
}
Комбинированный пример: Resize + Crop
Часто требуется сначала изменить размер изображения, а затем обрезать его до точных размеров (например, для превью новостей). Ниже представлена функция, которая делает это за один проход с помощью Core Graphics.
func resizeAndCrop(image: UIImage, to