NSAffineTransform — е клас от AppKit (macOS), който представлява афинно преобразувание в двумерната графика. Той капсулира матрица 3×3, която описва комбинация от преместване, мащабиране, завъртане и срязване на координати. Според Apple Documentation (2026), NSAffineTransform се използва широко в Core Graphics за трансформиране на пътища, точки и графични контексти на macOS.
Основни моменти
NSAffineTransform — е клас от рамката AppKit, достъпен само на macOS. Той представлява афинно преобразувание в двумерното пространство чрез матрица 3×3. За разлика от CGAffineTransform от Core Graphics (който е достъпен на iOS), NSAffineTransform е обектно-ориентирана обвивка на структурата CGAffineTransform, добавяща удобни методи за работа с NSBezierPath, NSPoint и NSSize.
Афинно преобразувание — е трансформация на равнината, която запазва отношението „точка лежи на права линия“ и успоредността на линиите. С други думи, след афинно преобразувание успоредните линии остават успоредни, а правите линии остават прави. Въпреки това, разстоянията, ъглите и площите могат да се променят: мащабирането, завъртането и срязването са специални случаи на афинни преобразувания.
На iOS аналогът на NSAffineTransform е структурата CGAffineTransform от Core Graphics. И двата API се основават на един и същ математически модел — матрица на афинно преобразувание 3×3, където последният ред винаги е [0, 0, 1]. Разликата е, че NSAffineTransform е клас (с retain/release в Objective-C), докато CGAffineTransform е структура (стойностен тип) в Swift и C.
NSAffineTransform поддържа четири основни типа трансформации: translate (преместване), scale (мащабиране), rotate (завъртане) и shear (срязване). Всеки тип е представен от отделен метод и може да бъде приложен както към съществуващ обект на трансформация, така и използван за създаване на нов.
| Операция | Метод на NSAffineTransform | Параметри | Визуален ефект |
|---|---|---|---|
| Translate | translateXBy:yBy: | deltaX, deltaY | Преместване на обект по осите X и Y |
| Scale | scaleBy: / scaleXBy:yBy: | factor / scaleX, scaleY | Увеличаване или намаляване на размерите |
| Rotate | rotateByDegrees: / rotateByRadians: | ъгъл в градуси или радиани | Завъртане около началото на координатите |
| Shear | Чрез setTransformStruct | компоненти m12, m21 на матрицата | Срязване на обект по оста |
Translate добавя преместване към координатите на всички точки на обекта. Параметрите deltaX и deltaY определят големината на преместването хоризонтално и вертикално. Положителна стойност на deltaX премества надясно, deltaY — нагоре. Translate не променя формата или размера на обекта — само неговата позиция.
Scale умножава координатите на точките по даден фактор. При равномерно мащабиране (един параметър) обектът се увеличава или намалява пропорционално. При неравномерно мащабиране формата се изкривява. Фактор 1.0 оставя размера непроменен, по-голям от 1.0 увеличава, от 0.0 до 1.0 намалява. Отрицателните стойности създават огледален образ.
Rotate завърта обекта около началото на координатите на даден ъгъл. Положителният ъгъл на завъртане съответства на въртене обратно на часовниковата стрелка. rotateByDegrees приема градуси (0–360), rotateByRadians — радиани (0–2π). Завъртането винаги се извършва спрямо точката (0, 0), следователно за завъртане около произволна точка, rotate трябва да се комбинира с translate.
Афинно преобразувание в 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 — преместването не се прилага, само мащабиране, завъртане и срязване. Това е полезно за трансформация на вектори и размери, които не трябва да бъдат премествани.
Метод transformBezierPath: създава нов обект NSBezierPath, всяка точка на който е трансформирана според матрицата на NSAffineTransform. Оригиналният път не се променя — създава се копие с трансформирани координати. Това е скъпа операция за сложни пътища с голям брой възли.
Метод set прилага трансформацията към текущия графичен контекст (NSGraphicsContext), модифицирайки неговия CTM (Current Transformation Matrix). След извикване на set, цялото последващо рисуване в този контекст ще бъде трансформирано. За временна трансформация използвайте saveGraphicsState / restoreGraphicsState преди и след set.
Нека разгледаме практически примери за използване на NSAffineTransform в macOS приложение за трансформация на графични обекти.
Примерът създава NSAffineTransform, който комбинира преместване, завъртане и мащабиране. Първо обектът се премества с (100, 50), след това се завърта на 45 градуса и накрая се увеличава 1.5 пъти. Редът е важен: rotate след translate дава завъртане спрямо новото начало на координатите.
import AppKit
let transform = NSAffineTransform()
transform.translateXBy(100, yBy: 50)
transform.rotateByDegrees(45)
transform.scaleBy(1.5)
// Прилагане към точка
let point = NSPoint(x: 10, y: 20)
let transformedPoint = transform.transform(point)
// Прилагане към размер
let size = NSSize(width: 100, height: 50)
let transformedSize = transform.transform(size)
Примерът показва прилагане на NSAffineTransform към графичен контекст чрез set и concat. След прилагане на трансформацията, цялото последващо рисуване се извършва в трансформираната координатна система. Запазването на графичното състояние чрез saveGraphicsState позволява връщане към оригиналната матрица.
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()
// Рисуване в трансформирана координатна система
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 с трансформирани координати. Оригиналният път остава непроменен, а трансформираното копие може да бъде рисувано отделно — това е полезно за създаване на повтарящи се елементи на различни позиции.
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 — е клас от AppKit (само macOS), а CGAffineTransform — е структура от Core Graphics (iOS и macOS). NSAffineTransform е обектно-ориентирана обвивка и предоставя методи за работа с NSBezierPath. CGAffineTransform е лека структура от стойностен тип, използвана с CGPath.
За завъртане около центъра изпълнете три стъпки: 1) translate до центъра на обекта (преместване на началото на координатите в центъра), 2) rotate на желания ъгъл, 3) translate обратно с отрицателния вектор на центъра. Комбинацията translate—rotate—translate дава завъртане около произволна точка, а не около началото на координатите.
Не, 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.
Резюме
invert анулира ефекта на всяко афинно преобразуваниеЩе разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също