NSAffineTransform は、2次元グラフィックスにおけるアフィン変換を表すAppKit(macOS)のクラスです。これは、座標の移動、スケーリング、回転、せん断の組み合わせを記述する3×3行列をカプセル化します。Appleのドキュメント(2026年)によると、NSAffineTransformはCore GraphicsでmacOS上のパス、ポイント、グラフィックスコンテキストを変換するために広く使用されています。
主要ポイント
NSAffineTransform は、AppKitフレームワークのクラスで、macOSでのみ利用可能です。3×3行列を通じて2次元空間でのアフィン変換を表します。Core GraphicsのCGAffineTransform(iOSで利用可能)とは異なり、NSAffineTransformはCGAffineTransform構造体のオブジェクト指向ラッパーであり、NSBezierPath、NSPoint、NSSizeを操作するための便利なメソッドを追加します。
アフィン変換 とは、「点が直線上にある」という関係と直線の平行性を保持する平面の変換です。言い換えると、アフィン変換後も平行線は平行のままで、直線は直線のままです。ただし、距離、角度、面積は変化する可能性があります。スケーリング、回転、せん断はアフィン変換の特殊なケースです。
iOS では、NSAffineTransformに相当するのはCore GraphicsのCGAffineTransform構造体です。どちらのAPIも同じ数学的モデルに基づいています — 最後の行が常に[0, 0, 1]である3×3アフィン変換行列です。違いは、NSAffineTransformがクラス(Objective-Cではretain/release)であるのに対し、CGAffineTransformはSwiftとCの構造体(値型)であることです。
NSAffineTransform は、translate(移動)、scale(スケーリング)、rotate(回転)、shear(せん断)の4つの基本タイプの変換をサポートしています。各タイプは個別のメソッドで表され、既存の変換オブジェクトに適用することも、新しいものを作成するために使用することもできます。
| 操作 | 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でのアフィン変換は、最後の行が常に[0, 0, 1]である3×3行列で表されます。これは、変換の組み合わせを行列の積として表現できる標準的な数学的表現です。行列構造はNSAffineTransformStructを介して定義され、m11、m12、m21、m22、tX、tYの6つの重要な成分を含みます。
m11、m12、m21、m22の成分は、スケーリング、回転、せん断を担当します。tXとtYの成分は移動を担当します。標準構成(単位行列)では、m11 = 1、m22 = 1、その他は0です。現在の行列値を取得するには、transformStruct — NSAffineTransformStruct構造体を返すNSAffineTransformのプロパティを使用します。
行列の乗算は非可換演算です。つまり、変換を適用する順序が重要です:translate + rotateはrotate + translateとは異なる結果になります。結合する際、NSAffineTransformは新しい行列を現在の行列の左側に乗算します:newMatrix = operation × currentMatrix。したがって、最後に追加された変換が最初に適用されます。
NSAffineTransform は、複数の変換を1つのオブジェクトに結合できます。translateXBy:yBy:、scaleBy:、またはrotateByDegrees:の各呼び出しは、内部行列を変更します — 現在の行列を対応する変換の行列で乗算します。これにより、単純な操作のシーケンスから複雑な変換を作成できます。
合成された変換を反転するには、invertメソッドを使用します。逆変換は元の変換を打ち消す新しいアフィン変換を作成します。ポイントに元の変換を適用し、次に逆変換を適用すると、ポイントは元の位置に戻ります。反転は、変換されるべきでないUI要素を描画する前にコンテキスト変換を打ち消すのに役立ちます。
変換が恒等(座標を変更しない)かどうかを確認するには、isIdentityプロパティを使用します。恒等変換は単位行列であり、乗算してもポイントの座標は変わりません。新しいNSAffineTransformオブジェクトは恒等変換として作成されます。このプロパティは最適化に役立ちます。変換が恒等の場合、その適用をスキップできます。
NSAffineTransform は、NSPoint、NSSize、NSBezierPath、およびNSGraphicsContextグラフィックスコンテキストの4種類のオブジェクトに適用できます。各ターゲットには個別のメソッドがあります:transformPoint:、transformSize:、transformBezierPath:、およびset。
メソッド transformPoint: は、変換行列をポイントの座標ベクトル(x, y, 1)で乗算し、ポイントの新しい位置を返します。transformSize: も同様に動作しますが、tXとtYの成分を考慮しません — 移動は適用されず、スケーリング、回転、せん断のみが適用されます。これは、移動されるべきでないベクトルやサイズの変換に役立ちます。
メソッド transformBezierPath: は、各ポイントがNSAffineTransform行列に従って変換される新しいNSBezierPathオブジェクトを作成します。元のパスは変更されません — 変換された座標を持つコピーが作成されます。これは多数のノードを持つ複雑なパスでは高コストな操作です。
メソッド set は、現在のグラフィックスコンテキスト(NSGraphicsContext)に変換を適用し、そのCTM(現在の変換行列)を変更します。setを呼び出した後、このコンテキストでの後続の描画はすべて変換されます。一時的な変換には、setの前後にsaveGraphicsState/restoreGraphicsStateを使用します。
グラフィックオブジェクトを変換するためのmacOSアプリケーションでのNSAffineTransformの使用の実践例を見てみましょう。
この例では、移動、回転、スケーリングを組み合わせたNSAffineTransformを作成します。最初に、オブジェクトは(100, 50)だけ移動され、次に45度回転され、最後に1.5倍にスケーリングされます。順序が重要です。移動後の回転は新しい原点を基準に回転します。
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)
この例は、setとconcatを介してグラフィックスコンテキストにNSAffineTransformを適用することを示しています。変換を適用した後、後続のすべての描画は変換された座標系で実行されます。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で使用される軽量な値型の構造体です。
中心の周りで回転するには、3つのステップを実行します: 1) オブジェクトの中心に移動(原点を中心に移動)、2) 目的の角度で回転、3) 負の中心ベクトルで戻す。移動—回転—移動の組み合わせにより、原点ではなく任意の点を中心とした回転が得られます。
いいえ、NSAffineTransformはAppKitの一部としてmacOSでのみ利用可能です。iOSでは、Core GraphicsのCGAffineTransform構造体を使用します。これにより、CGAffineTransformMakeTranslation、CGAffineTransformMakeScale、CGAffineTransformMakeRotation、CGAffineTransformConcatなどの同様の機能が提供されます。
invert メソッドを使用します。これにより逆変換が返されます。元の変換と逆変換を乗算すると、単位行列(恒等変換)が得られます。一時的な適用には、setの前にsaveGraphicsState、後にrestoreGraphicsStateを呼び出します。
負のスケーリング を使用します:transform.scaleXBy(-1, yBy: 1)は水平反射を、scaleXBy(1, yBy: -1)は垂直反射を作成します。任意の軸に対する反射には、translate(軸への移動)、-1のscale、および逆translateを組み合わせます。
まとめ
invertメソッドによる反転で任意のアフィン変換を取り消しターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。