NSAffineTransform — nó là gì, các loại biến đổi và cách áp dụng

Tác giả: IT Sectr Đã đăng: 2026-07-22 Thời gian đọc: 9 phút

NSAffineTransform là một lớp từ AppKit (macOS) biểu diễn phép biến đổi affine trong đồ họa hai chiều. Nó đóng gói một ma trận 3×3 mô tả sự kết hợp của phép tịnh tiến, tỷ lệ, xoay và cắt của tọa độ. Theo Tài liệu Apple (2026), NSAffineTransform được sử dụng rộng rãi trong Core Graphics để biến đổi đường dẫn, điểm và ngữ cảnh đồ họa trên macOS.

Những điểm chính

  • NSAffineTransform là lớp AppKit cho các phép biến đổi affine trên macOS với ma trận 3×3
  • Phép biến đổi affine bảo toàn tính song song của các đường nhưng có thể thay đổi khoảng cách và góc
  • Các thao tác cơ bản: translate (tịnh tiến), scale (tỷ lệ), rotate (xoay), shear (cắt)
  • Các biến đổi được kết hợp thông qua phép nhân ma trận — thứ tự thao tác ảnh hưởng đến kết quả
  • NSAffineTransform được áp dụng cho điểm, kích thước, đường dẫn và ngữ cảnh Core Graphics

NSAffineTransform là gì?

NSAffineTransform là một lớp từ framework AppKit, chỉ khả dụng trên macOS. Nó biểu diễn một phép biến đổi affine trong không gian hai chiều thông qua ma trận 3×3. Không giống như CGAffineTransform từ Core Graphics (có sẵn trên iOS), NSAffineTransform là một wrapper hướng đối tượng bao quanh cấu trúc CGAffineTransform, thêm các phương thức tiện lợi để làm việc với NSBezierPath, NSPoint và NSSize.

Phép biến đổi affine là một phép biến đổi của mặt phẳng bảo toàn mối quan hệ “điểm nằm trên đường thẳng” và tính song song của các đường. Nói cách khác, sau phép biến đổi affine, các đường song song vẫn song song và các đường thẳng vẫn thẳng. Tuy nhiên, khoảng cách, góc và diện tích có thể thay đổi: tỷ lệ, xoay và cắt là các trường hợp đặc biệt của phép biến đổi affine.

Trên iOS, tương đương với NSAffineTransform là cấu trúc CGAffineTransform từ Core Graphics. Cả hai API đều dựa trên cùng một mô hình toán học — ma trận biến đổi affine 3×3 với hàng cuối cùng luôn là [0, 0, 1]. Sự khác biệt là NSAffineTransform là một lớp (với retain/release trong Objective-C), trong khi CGAffineTransform là một cấu trúc (kiểu giá trị) trong Swift và C.

Các loại biến đổi affine

NSAffineTransform hỗ trợ bốn loại biến đổi cơ bản: translate (tịnh tiến), scale (tỷ lệ), rotate (xoay) và shear (cắt). Mỗi loại được biểu diễn bằng một phương thức riêng và có thể được áp dụng cho một đối tượng biến đổi hiện có hoặc được sử dụng để tạo một đối tượng mới.

Thao tácPhương thức NSAffineTransformTham sốHiệu ứng trực quan
TranslatetranslateXBy:yBy:deltaX, deltaYDi chuyển đối tượng dọc theo trục X và Y
ScalescaleBy: / scaleXBy:yBy:factor / scaleX, scaleYPhóng to hoặc thu nhỏ kích thước
RotaterotateByDegrees: / rotateByRadians:góc tính bằng độ hoặc radianXoay quanh gốc tọa độ
ShearThông qua setTransformStructthành phần ma trận m12, m21Nghiêng đối tượng dọc theo trục

Translate — Tịnh tiến

Translate thêm một độ dời vào tọa độ của tất cả các điểm của đối tượng. Tham số deltaX và deltaY xác định lượng dịch chuyển ngang và dọc tương ứng. Giá trị deltaX dương dịch sang phải, deltaY dương dịch lên trên. Translate không thay đổi hình dạng hoặc kích thước của đối tượng — chỉ thay đổi vị trí của nó.

Scale — Tỷ lệ

Scale nhân tọa độ của các điểm với một hệ số cho trước. Với tỷ lệ đồng nhất (một tham số), đối tượng phóng to hoặc thu nhỏ theo tỷ lệ. Với tỷ lệ không đồng nhất, hình dạng bị méo. Hệ số 1.0 giữ nguyên kích thước, lớn hơn 1.0 phóng to, từ 0.0 đến 1.0 thu nhỏ. Giá trị âm tạo ra phản chiếu gương.

Rotate — Xoay

Rotate xoay đối tượng quanh gốc tọa độ một góc cho trước. Góc xoay dương tương ứng với xoay ngược chiều kim đồng hồ. rotateByDegrees chấp nhận độ (0–360), rotateByRadians chấp nhận radian (0–2π). Xoay luôn được thực hiện so với điểm (0, 0), vì vậy để xoay quanh một điểm bất kỳ, cần kết hợp rotate với translate.

Biểu diễn ma trận 3×3

Phép biến đổi affine trong NSAffineTransform được biểu diễn bằng ma trận 3×3 với hàng cuối cùng luôn là [0, 0, 1]. Đây là biểu diễn toán học tiêu chuẩn cho phép biểu diễn sự kết hợp của các biến đổi dưới dạng tích của ma trận. Cấu trúc ma trận được định nghĩa thông qua NSAffineTransformStruct, chứa sáu thành phần quan trọng: m11, m12, m21, m22, tX, tY.

Các thành phần m11, m12, m21, m22 chịu trách nhiệm cho tỷ lệ, xoay và cắt. Các thành phần tXtY chịu trách nhiệm cho tịnh tiến. Trong cấu hình tiêu chuẩn (ma trận đơn vị), m11 = 1, m22 = 1, và các thành phần còn lại bằng 0. Để lấy giá trị hiện tại của ma trận, sử dụng transformStruct — thuộc tính của NSAffineTransform trả về cấu trúc NSAffineTransformStruct.

Phép nhân ma trận là một phép toán không giao hoán. Điều này có nghĩa là thứ tự áp dụng các biến đổi rất quan trọng: translate + rotate cho kết quả khác với rotate + translate. Khi kết hợp, NSAffineTransform nhân ma trận mới ở bên trái với ma trận hiện tại: newMatrix = operation × currentMatrix. Do đó, biến đổi được thêm cuối cùng được áp dụng đầu tiên.

Kết hợp các biến đổi

NSAffineTransform cho phép kết hợp nhiều biến đổi trong một đối tượng. Mỗi lần gọi translateXBy:yBy:, scaleBy: hoặc rotateByDegrees: sẽ sửa đổi ma trận bên trong — nhân ma trận hiện tại với ma trận của biến đổi tương ứng. Điều này cho phép tạo một biến đổi phức tạp từ một chuỗi các thao tác đơn giản.

Để đảo ngược một biến đổi kết hợp, sử dụng phương thức invert. Phép đảo ngược tạo ra một biến đổi affine mới hủy bỏ biến đổi gốc: nếu bạn áp dụng biến đổi gốc cho một điểm và sau đó áp dụng biến đổi đảo ngược, điểm đó sẽ trở về vị trí ban đầu. Đảo ngược hữu ích để hủy bỏ biến đổi ngữ cảnh trước khi vẽ các phần tử UI không nên bị biến đổi.

Để kiểm tra xem một biến đổi có phải là đồng nhất (không thay đổi tọa độ) hay không, sử dụng thuộc tính isIdentity. Biến đổi đồng nhất là ma trận đơn vị — nhân với nó không thay đổi tọa độ của điểm. Một đối tượng NSAffineTransform mới được tạo ra như một biến đổi đồng nhất. Thuộc tính này hữu ích cho việc tối ưu hóa: nếu biến đổi là đồng nhất, có thể bỏ qua việc áp dụng nó.

Áp dụng cho đối tượng và ngữ cảnh

NSAffineTransform có thể được áp dụng cho bốn loại đối tượng: NSPoint, NSSize, NSBezierPath và ngữ cảnh đồ họa NSGraphicsContext. Mỗi mục tiêu có một phương thức riêng: transformPoint:, transformSize:, transformBezierPath:set.

Biến đổi điểm và kích thước

Phương thức transformPoint: nhân ma trận biến đổi với vector tọa độ của điểm (x, y, 1), trả về vị trí mới của điểm. transformSize: hoạt động tương tự nhưng không tính đến các thành phần tX và tY — tịnh tiến không được áp dụng, chỉ có tỷ lệ, xoay và cắt. Điều này hữu ích cho việc biến đổi các vector và kích thước không nên bị dịch chuyển.

Biến đổi đường dẫn NSBezierPath

Phương thức transformBezierPath: tạo một đối tượng NSBezierPath mới, mỗi điểm của nó được biến đổi theo ma trận NSAffineTransform. Đường dẫn gốc không bị thay đổi — một bản sao với tọa độ đã biến đổi được tạo ra. Đây là một thao tác tốn kém đối với các đường dẫn phức tạp với số lượng nút lớn.

Áp dụng cho ngữ cảnh đồ họa

Phương thức set áp dụng biến đổi cho ngữ cảnh đồ họa hiện tại (NSGraphicsContext), sửa đổi CTM (Ma trận biến đổi hiện tại) của nó. Sau khi gọi set, tất cả các thao tác vẽ tiếp theo trong ngữ cảnh này sẽ bị biến đổi. Đối với các biến đổi tạm thời, hãy sử dụng saveGraphicsState / restoreGraphicsState trước và sau set.

Ví dụ mã Swift

Hãy xem các ví dụ thực tế về việc sử dụng NSAffineTransform trong ứng dụng macOS để biến đổi các đối tượng đồ họa.

Tạo và kết hợp các biến đổi

Ví dụ này tạo một NSAffineTransform kết hợp tịnh tiến, xoay và tỷ lệ. Đầu tiên, đối tượng được dịch chuyển (100, 50), sau đó xoay 45 độ, và cuối cùng được phóng to 1.5 lần. Thứ tự rất quan trọng: xoay sau khi tịnh tiến sẽ xoay so với gốc tọa độ mới.

swift
import AppKit

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

// Áp dụng cho điểm
let point = NSPoint(x: 10, y: 20)
let transformedPoint = transform.transform(point)

// Áp dụng cho kích thước
let size = NSSize(width: 100, height: 50)
let transformedSize = transform.transform(size)

Biến đổi ngữ cảnh để vẽ

Ví dụ này minh họa việc áp dụng NSAffineTransform cho ngữ cảnh đồ họa thông qua setconcat. Sau khi áp dụng biến đổi, tất cả các thao tác vẽ tiếp theo được thực hiện trong hệ tọa độ đã biến đổi. Lưu trạng thái đồ họa qua saveGraphicsState cho phép quay lại ma trận gốc.

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()

    // Vẽ trong hệ tọa độ đã biến đổi
    let path = NSBezierPath(roundedRect: NSRect(
        x: -50, y: -50,
        width: 100, height: 100
    ), xRadius: 10, yRadius: 10)
    NSColor.blue.setFill()
    path.fill()

    ctx.restoreGraphicsState()
}

Biến đổi NSBezierPath

Ví dụ này minh họa việc tạo bản sao của NSBezierPath với tọa độ đã biến đổi. Đường dẫn gốc không thay đổi và bản sao đã biến đổi có thể được vẽ riêng biệt — điều này hữu ích để tạo các phần tử lặp lại ở các vị trí khác nhau.

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
}

Câu hỏi thường gặp

Sự khác biệt giữa NSAffineTransform và CGAffineTransform là gì?

NSAffineTransform là một lớp từ AppKit (chỉ macOS), trong khi CGAffineTransform là một cấu trúc từ Core Graphics (iOS và macOS). NSAffineTransform là một wrapper hướng đối tượng và cung cấp các phương thức để làm việc với NSBezierPath. CGAffineTransform là một cấu trúc kiểu giá trị nhẹ được sử dụng với CGPath.

Làm thế nào để xoay một đối tượng quanh tâm của nó?

Để xoay quanh tâm, thực hiện ba bước: 1) tịnh tiến đến tâm của đối tượng (di chuyển gốc tọa độ vào tâm), 2) xoay một góc mong muốn, 3) tịnh tiến ngược lại bằng vector tâm âm. Sự kết hợp tịnh tiến—xoay—tịnh tiến tạo ra phép xoay quanh một điểm bất kỳ, thay vì quanh gốc tọa độ.

Tôi có thể sử dụng NSAffineTransform trên iOS không?

Không, NSAffineTransform chỉ khả dụng trên macOS trong AppKit. Trên iOS, sử dụng cấu trúc CGAffineTransform từ Core Graphics, cung cấp các khả năng tương tự: CGAffineTransformMakeTranslation, CGAffineTransformMakeScale, CGAffineTransformMakeRotation và CGAffineTransformConcat.

Làm thế nào để hủy bỏ một biến đổi?

Sử dụng phương thức invert, trả về biến đổi đảo ngược. Nếu bạn nhân biến đổi gốc với biến đổi đảo ngược, bạn nhận được ma trận đơn vị (biến đổi đồng nhất). Để áp dụng tạm thời, hãy gọi saveGraphicsState trước setrestoreGraphicsState sau.

Làm thế nào để tạo phản chiếu gương?

Sử dụng tỷ lệ âm: transform.scaleXBy(-1, yBy: 1) tạo phản chiếu ngang, trong khi scaleXBy(1, yBy: -1) tạo phản chiếu dọc. Để phản chiếu so với một trục bất kỳ, kết hợp translate (dịch chuyển đến trục), scale với -1 và translate ngược.

Tổng kết

  • NSAffineTransform là lớp AppKit cho các biến đổi affine với ma trận 3×3 trên macOS
  • Bốn loại cơ bản: translate (tịnh tiến), scale (tỷ lệ), rotate (xoay) và shear (cắt)
  • Ma trận 3×3 chứa sáu thành phần quan trọng: m11, m12, m21, m22, tX, tY
  • Thứ tự kết hợp các biến đổi ảnh hưởng đến kết quả — phép nhân ma trận không giao hoán
  • Áp dụng cho NSPoint, NSSize, NSBezierPath và ngữ cảnh đồ họa qua CTM
  • Đảo ngược qua phương thức invert hủy bỏ mọi biến đổi affine
  • Trên iOS, tương đương là cấu trúc CGAffineTransform từ Core Graphics

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm