Работа с изображениями на Swift: resize, crop (iOS/macOS)

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Работа с изображениями: 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                    

Рекомендаций