NSAffineTransform — что это, виды трансформаций и как применять

Автор: IT Sectr Опубликовано: 2026-07-22 Время чтения: 9 мин

NSAffineTransform — это класс из AppKit (macOS), представляющий аффинное преобразование в двумерной графике. Он инкапсулирует матрицу 3×3, которая описывает комбинацию перемещения, масштабирования, поворота и сдвига координат. По данным Apple Documentation (2026), NSAffineTransform широко используется в Core Graphics для трансформации путей, точек и графических контекстов на macOS.

Главное

  • NSAffineTransform — класс AppKit для аффинных преобразований на macOS с матрицей 3×3
  • Аффинное преобразование сохраняет параллельность линий, но может менять расстояния и углы
  • Основные операции: translate (сдвиг), scale (масштаб), rotate (поворот), shear (сдвиг)
  • Преобразования комбинируются через умножение матриц — порядок операций влияет на результат
  • NSAffineTransform применяется к точкам, размерам, путям и графическому контексту Core Graphics

Что такое NSAffineTransform?

NSAffineTransform — это класс из фреймворка AppKit, доступный только на macOS. Он представляет аффинное преобразование в двумерном пространстве через матрицу 3×3. В отличие от CGAffineTransform из Core Graphics (который доступен на iOS), NSAffineTransform является объектно-ориентированной обёрткой над struct CGAffineTransform, добавляя удобные методы для работы с NSBezierPath, NSPoint и NSSize.

Аффинное преобразование — это такое преобразование плоскости, которое сохраняет отношение "точка лежит на прямой" и параллельность прямых. Другими словами, после аффинного преобразования параллельные линии остаются параллельными, а прямые — прямыми. Однако расстояния, углы и площади могут меняться: масштабирование, поворот и сдвиг являются частными случаями аффинных преобразований.

На iOS аналогом NSAffineTransform является структура CGAffineTransform из Core Graphics. Оба API основаны на одной математической модели — матрице аффинного преобразования 3×3, где последняя строка всегда [0, 0, 1]. Различие в том, что NSAffineTransform — это класс (с retain/release в Objective-C), а CGAffineTransform — это struct (значимый тип) в Swift и C.

Типы аффинных преобразований

NSAffineTransform поддерживает четыре базовых типа преобразований: translate (перемещение), scale (масштабирование), rotate (поворот) и shear (сдвиг/наклон). Каждый тип представлен отдельным методом и может быть применён как к существующему объекту трансформации, так и использован для создания новой.

ОперацияМетод NSAffineTransformПараметрыВизуальный эффект
TranslatetranslateXBy:yBy:deltaX, deltaYСмещение объекта по осям X и Y
ScalescaleBy: / scaleXBy:yBy:factor / scaleX, scaleYУвеличение или уменьшение размеров
RotaterotateByDegrees: / rotateByRadians:angle в градусах или радианахПоворот вокруг начала координат
ShearЧерез setTransformStructm12, m21 компоненты матрицыНаклон объекта вдоль оси

Translate — перемещение

Translate добавляет смещение к координатам всех точек объекта. Параметры deltaX и deltaY задают величину сдвига по горизонтали и вертикали соответственно. Положительное значение deltaX смещает вправо, deltaY — вверх. Translate не меняет форму и размер объекта — только его положение.

Scale — масштабирование

Scale умножает координаты точек на заданный коэффициент. При равномерном масштабировании (один параметр) объект увеличивается или уменьшается пропорционально. При неравномерном — искажается форма. Коэффициент 1.0 оставляет размер без изменений, больше 1.0 — увеличивает, от 0.0 до 1.0 — уменьшает. Отрицательные значения создают зеркальное отражение.

Rotate — поворот

Rotate поворачивает объект вокруг начала координат на заданный угол. Положительный угол поворота соответствует вращению против часовой стрелки. rotateByDegrees принимает градусы (0–360), rotateByRadians — радианы (0–2π). Поворот всегда выполняется относительно точки (0, 0), поэтому для поворота вокруг произвольной точки необходимо комбинировать rotate с translate.

Матричное представление 3×3

Аффинное преобразование в NSAffineTransform представляется матрицей 3×3, где последняя строка всегда [0, 0, 1]. Это стандартное математическое представление, позволяющее представлять комбинацию преобразований как произведение матриц. Структура матрицы определяется через NSAffineTransformStruct, содержащую шесть значимых компонентов: m11, m12, m21, m22, tX, tY.

Компоненты m11, m12, m21, m22 отвечают за масштабирование, поворот и сдвиг. Компоненты tX и tY — за перемещение. В стандартной конфигурации (единичная матрица) m11 = 1, m22 = 1, остальные равны 0. Для получения текущих значений матрицы используется transformStruct — свойство NSAffineTransform, возвращающее структуру NSAffineTransformStruct.

Умножение матриц — некоммутативная операция. Это означает, что порядок применения преобразований имеет значение: translate + rotate даёт другой результат, чем rotate + translate. При комбинировании NSAffineTransform умножает новую матрицу слева на текущую: newMatrix = operation × currentMatrix. Поэтому последнее добавленное преобразование применяется первым.

Комбинирование преобразований

NSAffineTransform позволяет комбинировать несколько преобразований в одном объекте. Каждый вызов translateXBy:yBy:, scaleBy: или rotateByDegrees: модифицирует внутреннюю матрицу — умножает текущую матрицу на матрицу соответствующего преобразования. Это позволяет создать сложное преобразование из последовательности простых операций.

Для инвертирования комбинированного преобразования используется метод invert. Инверсия создаёт новое аффинное преобразование, которое отменяет исходное: если применить к точке исходное преобразование, а затем инвертированное, точка вернётся в начальное положение. Инверсия полезна для отмены трансформации контекста перед отрисовкой UI-элементов, которые не должны быть трансформированы.

Проверить, является ли преобразование тождественным (не меняет координаты), можно через свойство isIdentity. Тождественное преобразование — это единичная матрица, при умножении на которую координаты точки не меняются. Новый объект NSAffineTransform создаётся как тождественное преобразование. Это свойство полезно для оптимизации: если преобразование тождественное, его применение можно пропустить.

Применение к объектам и контексту

NSAffineTransform может быть применён к четырём типам объектов: NSPoint, NSSize, NSBezierPath и графическому контексту NSGraphicsContext. Для каждой цели существует отдельный метод: transformPoint:, transformSize:, transformBezierPath: и set.

Трансформация точек и размеров

Метод transformPoint: умножает матрицу преобразования на вектор координат точки (x, y, 1), возвращая новое положение точки. transformSize: работает аналогично, но не учитывает компонент tX и tY — смещение не применяется, только масштабирование, поворот и сдвиг. Это полезно для трансформации векторов и размеров, которые не должны сдвигаться.

Трансформация путей NSBezierPath

Метод transformBezierPath: создаёт новый объект NSBezierPath, каждая точка которого трансформирована в соответствии с матрицей NSAffineTransform. Исходный путь не изменяется — создаётся копия с трансформированными координатами. Это дорогая операция для сложных путей с большим количеством узлов.

Применение к графическому контексту

Метод set применяет преобразование к текущему графическому контексту (NSGraphicsContext), модифицируя его CTM (Current Transformation Matrix). После вызова set вся последующая отрисовка в этом контексте будет трансформироваться. Для временной трансформации используйте saveGraphicsState / restoreGraphicsState до и после set.

Примеры кода на Swift

Рассмотрим практические примеры использования NSAffineTransform в macOS-приложении для трансформации графических объектов.

Создание и комбинирование преобразований

Пример создаёт NSAffineTransform, комбинирующий сдвиг, поворот и масштабирование. Сначала объект смещается на (100, 50), затем поворачивается на 45 градусов и, наконец, увеличивается в 1.5 раза. Важен порядок: rotate после translate — поворот будет относительно нового начала координат.

swift
import AppKit

let transform = NSAffineTransform()
transform.translateXBy(100, yBy: 50)
transform.rotateByDegrees(45)
transform.scaleBy(1.5)

// Applying to point
let point = NSPoint(x: 10, y: 20)
let transformedPoint = transform.transform(point)

// Applying to size
let size = NSSize(width: 100, height: 50)
let transformedSize = transform.transform(size)

Трансформация контекста для отрисовки

Пример показывает применение NSAffineTransform к графическому контексту через set и concat. После применения трансформации вся последующая отрисовка выполняется в трансформированной системе координат. Сохранение состояния графики через saveGraphicsState позволяет вернуться к исходной матрице.

swift
func drawWithTransform(in rect: NSRect) {
    guard let ctx = NSGraphicsContext.current
    else { return }

    ctx.saveGraphicsState()

    let xfm = NSAffineTransform()
    xfm.translateXBy(rect.midX, yBy: rect.midY)
    xfm.rotateByDegrees(30)
    xfm.scaleBy(0.8)
    xfm.concat()

    // Drawing in transformed coordinate system
    let path = NSBezierPath(roundedRect: NSRect(
        x: -50, y: -50,
        width: 100, height: 100
    ), xRadius: 10, yRadius: 10)
    NSColor.blue.setFill()
    path.fill()

    ctx.restoreGraphicsState()
}

Трансформация пути NSBezierPath

Пример демонстрирует создание копии NSBezierPath с трансформированными координатами. Исходный путь остаётся без изменений, а трансформированная копия может быть отрисована отдельно — это полезно для создания повторяющихся элементов в разных положениях.

swift
func createTransformedTriangle() -> NSBezierPath {
    let triangle = NSBezierPath()
    triangle.move(to: NSPoint(x: 0, y: 0))
    triangle.line(to: NSPoint(x: 100, y: 0))
    triangle.line(to: NSPoint(x: 50, y: 100))
    triangle.close()

    let xfm = NSAffineTransform()
    xfm.translateXBy(200, yBy: 100)
    xfm.scaleXBy(1.5, yBy: 1.5)
    xfm.rotateByDegrees(45)

    let transformed = xfm.transform(triangle)
    return transformed
}

Часто задаваемые вопросы

Чем отличается NSAffineTransform от CGAffineTransform?

NSAffineTransform — это класс из AppKit (только macOS), а CGAffineTransform — это структура из Core Graphics (iOS и macOS). NSAffineTransform является объектно-ориентированной обёрткой и предоставляет методы для работы с NSBezierPath. CGAffineTransform — легковесная value-тип структура, используемая с CGPath.

Как повернуть объект вокруг его центра?

Для поворота вокруг центра выполните три шага: 1) translate на центр объекта (смещение начала координат в центр), 2) rotate на нужный угол, 3) translate обратно на отрицательный вектор центра. Комбинация translate—rotate—translate даёт вращение вокруг произвольной точки, а не вокруг начала координат.

Можно ли использовать NSAffineTransform на iOS?

Нет, NSAffineTransform доступен только на macOS в составе AppKit. На iOS используется структура CGAffineTransform из Core Graphics, которая предоставляет аналогичные возможности: CGAffineTransformMakeTranslation, CGAffineTransformMakeScale, CGAffineTransformMakeRotation и CGAffineTransformConcat.

Как отменить преобразование?

Используйте метод invert, который возвращает обратное преобразование. Если умножить исходное преобразование на инвертированное, получится единичная матрица (тождественное преобразование). Для временного применения вызовите saveGraphicsState до set и restoreGraphicsState после.

Как создать зеркальное отражение?

Используйте отрицательный масштаб: transform.scaleXBy(-1, yBy: 1) создаёт отражение по горизонтали, а scaleXBy(1, yBy: -1) — по вертикали. Для отражения относительно произвольной оси скомбинируйте translate (смещение на ось), scale с -1 и обратный translate.

Итоги

  • NSAffineTransform — класс AppKit для аффинных преобразований с матрицей 3×3 на macOS
  • Четыре базовых типа: translate (сдвиг), scale (масштаб), rotate (поворот) и shear (наклон)
  • Матрица 3×3 содержит шесть значимых компонентов: m11, m12, m21, m22, tX, tY
  • Порядок комбинирования преобразований влияет на результат — умножение матриц некоммутативно
  • Применяется к NSPoint, NSSize, NSBezierPath и графическому контексту через CTM
  • Инверсия через метод invert отменяет действие любого аффинного преобразования
  • На iOS аналогом является структура CGAffineTransform из Core Graphics

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также