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

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

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

Начать курс

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

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