draw(_:) / drawRect は、UIViewクラス(Swift)のメソッドと、Objective-Cでの対応物(drawRect:)であり、Core Graphicsを使用してビューのコンテンツをレンダリングします。システムは、ビューが画面に最初に表示されたとき、および setNeedsDisplay() の呼び出し後に、自動的にこのメソッドを呼び出します。Apple Documentation(2025)によると、draw(_:) は、開発者がカスタム描画のために現在の画面のグラフィックコンテキストにアクセスできる唯一のメソッドです。draw(_:) をオーバーライドすると、コンポーネントの外観を完全に制御できます — 単純な幾何学図形から複雑なアニメーショングラフィックスまで。
重要なポイント
draw(_:) は、UIKitがビューのコンテンツをレンダリングするために呼び出すUIViewのインスタンスメソッドです。このメソッド内で、開発者は CGContext グラフィックコンテキストにアクセスし、Core Graphics APIを使用して線、塗りつぶし、テキスト、画像を描画します。Objective-Cの drawRect: は同じ機能を実行しますが、構文が異なります。唯一のパラメータは、再描画する領域を指定するCGRectです。
Apple Engineering(2024)によると、draw(_:) はCPUベースのビットマップグラフィックコンテキストレンダリングを介して動作し、最大の柔軟性を提供しますが、CALayerと比較してより多くのリソースを必要とします。draw(_:) の使用決定は、グラフィックの複雑さとパフォーマンス要件に基づいて行われます。
Swiftでは、メソッドは override func draw(_ rect: CGRect) として宣言され、rect は再描画が必要な矩形です。Objective-Cでは、シグネチャは - (void)drawRect:(CGRect)rect です。setNeedsDisplay(_:) による部分的な再描画時には、rectパラメータがビューのboundsよりも小さくなることがあります。
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
システムは、厳密に定義されたシナリオで自動的に draw(_:) を呼び出します。これらのトリガーを理解することで、不要な再描画を回避し、ビューのパフォーマンスを最適化できます。以下に、メソッドの自動呼び出しのすべてのケースを示します。
Appleのドキュメントは警告しています:draw(_:) を直接呼び出さないでください。システム自身が描画を実行するタイミングを決定し、直接呼び出しは内部キャッシュメカニズムを破壊します。再描画を要求するには、常に setNeedsDisplay() または setNeedsDisplay(_:) を使用してください。
Swiftで draw(_:) をオーバーライドするには、グラフィックコンテキストを取得し、続けてCore Graphicsを呼び出します。描画の論理ブロックごとに個別のメソッドを作成することをお勧めします — これによりコードの可読性とテスト容易性が向上します。
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
// Line parameters
context.setStrokeColor(UIColor.darkGray.cgColor)
context.setLineWidth(2.0)
// Drawing a triangle
context.move(to: CGPoint(x: rect.midX, y: rect.minY + 10))
context.addLine(to: CGPoint(x: rect.maxX - 10, y: rect.maxY - 10))
context.addLine(to: CGPoint(x: rect.minX + 10, y: rect.maxY - 10))
context.closePath()
context.strokePath()
}
この例では、draw(_:) は2ピクセルの太さの濃い灰色の輪郭を持つ三角形を描画します。メソッドの先頭で super.draw(rect) を呼び出すことはAppleによって推奨されています。親の描画ロジックを維持するためですが、UIViewのデフォルトの draw(_:) 実装は空です。
draw(_:) の各呼び出しは、同じ入力データに対して同じ結果を生成する必要があります。これにより、システムは結果をキャッシュし、ビューのコンテンツが変更されていない場合は draw(_:) を再度呼び出さないようにできます。draw(_:) 内でランダムな値、システム時刻、ネットワークリクエストを 使用しないでください。
drawRect: は歴史的に最初のバージョンのメソッドであり、iOS 2.0でObjective-Cとともに登場しました。Swiftでは、外部パラメータ _ を使用してメソッド名が draw(_:) に変更されました。機能的には、これらのメソッドは同じです。どちらも再描画する領域のCGRectを受け取り、UIGraphicsGetCurrentContext() を使用してグラフィックコンテキストにアクセスします。
| 特性 | draw(_:) (Swift) | drawRect: (Objective-C) |
|---|---|---|
| シグネチャ | override func draw(_ rect: CGRect) | - (void)drawRect:(CGRect)rect |
| super呼び出し | super.draw(rect) | [super drawRect:rect] |
| コンテキスト | UIGraphicsGetCurrentContext() | UIGraphicsGetCurrentContext() |
| rectパラメータ | rect: CGRect | CGRect rect |
| パフォーマンス | 同一 | 同一 |
Objective-CからSwiftにプロジェクトを移行する際、メソッド名の変更は最初のタスクの一つです。Xcodeは自動変換機能を提供しますが、drawRect: は手動で draw(_:) に更新する必要があります。Apple(2024)によると、Swiftバージョンの draw(_:) が新しいプロジェクトに推奨されます。
draw(_:) はCPU上で実行され、最適でない実装はフレームドロップやパフォーマンス低下の原因となる可能性があります。Appleは描画を高速化するためのいくつかの実証済みのアプローチを推奨しています。
各Core Graphics操作(move(to:), addLine(to:), strokePath)にはオーバーヘッドがあります。操作をグループ化し、複雑な形状には CGPath を使用してください — パスは一度作成され、draw(_:) の呼び出しごとに再利用されます。
UIBezierPath はCGPathのObjective-Cラッパーであり、形状を作成するためのシンプルなAPIを提供します。UIBezierPathを事前に作成し(例:初期化子で)、draw(_:) 内で単に fill() または stroke() を呼び出します。
private let starPath: UIBezierPath = {
let path = UIBezierPath()
// Building a star shape
path.move(to: CGPoint(x: 50, y: 0))
for i in 1...5 {
let angle = CGFloat(i) * 4 * CGFloat.pi / 5
path.addLine(to: CGPoint(x: 50 + 40 * cos(angle),
y: 50 + 40 * sin(angle)))
}
path.close()
return path
}()
override func draw(_ rect: CGRect) {
UIColor.systemYellow.setFill()
starPath.fill()
}
開発者は draw(_:) をオーバーライドする際に同じ間違いをよく犯します。これらのパターンを認識することで、バグやパフォーマンスの低下を回避できます。最も一般的な問題とその解決策を見てみましょう。
これらのルールに従うことで、どのiOSプロジェクトでも安定した高速なカスタムビュー描画を確保できます。実際の実行時間とボトルネックを確認するには、Instruments(Core Animation)を使用して draw(_:) を プロファイリング してください。
よくある質問
いいえ、draw(_:) の直接呼び出しはAppleのドキュメントで禁止されています。システム自身が描画サイクルを管理します。再描画を要求するには、setNeedsDisplay() を使用してください。これは最も近いレンダリングサイクルでビューを更新が必要として正しくマークします。
機能的にはこれらのメソッドは同一です。drawRect: はObjective-Cで使用され、draw(_:) はSwiftで使用されます。どちらも再描画する領域のCGRectを受け取り、UIGraphicsGetCurrentContext() を介して同じCore Graphicsコンテキストを使用します。
Appleはレンダリングを最適化しています:UIViewにオーバーライドされた draw(_:) がない場合、システムはそのためのビットマップコンテキストを作成しません。これによりメモリを節約できます。オーバーライドが存在するのにメソッドが呼び出されない場合は、ビューのフレームがゼロでなく、ビューが表示可能であることを確認してください。
必要な場合のみ:最初のレンダリング時、setNeedsDisplay() 後、contentMode = .redraw でboundsが変更された時。静的な状態では、draw(_:) は再度呼び出されず、CPUとバッテリーリソースを節約します。
Appleはオーバーライドされたメソッドの先頭で super.draw(rect) を呼び出すことを推奨しています。現在のUIViewの実装は空ですが、super呼び出しは将来のUIKitバージョンとの互換性を保証し、良い習慣です。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。