draw(_:) と drawRect — 概要、呼び出し、オーバーライド

著者: IT Sectr 公開日: 2026-07-20 読了時間: 7 分

draw(_:) / drawRect は、UIViewクラス(Swift)のメソッドと、Objective-Cでの対応物(drawRect:)であり、Core Graphicsを使用してビューのコンテンツをレンダリングします。システムは、ビューが画面に最初に表示されたとき、および setNeedsDisplay() の呼び出し後に、自動的にこのメソッドを呼び出します。Apple Documentation(2025)によると、draw(_:) は、開発者がカスタム描画のために現在の画面のグラフィックコンテキストにアクセスできる唯一のメソッドです。draw(_:) をオーバーライドすると、コンポーネントの外観を完全に制御できます — 単純な幾何学図形から複雑なアニメーショングラフィックスまで。

重要なポイント

  • draw(_:) — Core Graphicsを介したカスタム描画のためのUIViewメソッド、開発者から直接呼び出されない
  • drawRect: — Objective-Cでの draw(_:) に相当、構文的に異なるが機能的に同一
  • CGContext — すべての描画操作のために draw(_:) 内で利用可能なグラフィックコンテキスト
  • setNeedsDisplay() — 開発者側から draw(_:) の再描画を要求する唯一の正しい方法
  • UIGraphicsGetCurrentContext() — draw(_:) 内で現在のコンテキストを取得する関数、描画前に必須の手順

draw(_:) / drawRect とは

draw(_:) は、UIKitがビューのコンテンツをレンダリングするために呼び出すUIViewのインスタンスメソッドです。このメソッド内で、開発者は CGContext グラフィックコンテキストにアクセスし、Core Graphics APIを使用して線、塗りつぶし、テキスト、画像を描画します。Objective-Cの drawRect: は同じ機能を実行しますが、構文が異なります。唯一のパラメータは、再描画する領域を指定するCGRectです。

Apple Engineering(2024)によると、draw(_:) はCPUベースのビットマップグラフィックコンテキストレンダリングを介して動作し、最大の柔軟性を提供しますが、CALayerと比較してより多くのリソースを必要とします。draw(_:) の使用決定は、グラフィックの複雑さとパフォーマンス要件に基づいて行われます。

Swift と Objective-C でのメソッドシグネチャ

Swiftでは、メソッドは override func draw(_ rect: CGRect) として宣言され、rect は再描画が必要な矩形です。Objective-Cでは、シグネチャは - (void)drawRect:(CGRect)rect です。setNeedsDisplay(_:) による部分的な再描画時には、rectパラメータがビューのboundsよりも小さくなることがあります。

objective-c
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillRect(context, rect);
}

システムが draw(_:) を呼び出すタイミング

システムは、厳密に定義されたシナリオで自動的に draw(_:) を呼び出します。これらのトリガーを理解することで、不要な再描画を回避し、ビューのパフォーマンスを最適化できます。以下に、メソッドの自動呼び出しのすべてのケースを示します。

  • 最初のレンダリング — ビューが初めて階層に追加され、画面に表示される時
  • setNeedsDisplay() — このメソッドの呼び出し後、システムは次の描画サイクルで再描画する
  • setNeedsDisplay(_:) — 上記と同様だが、再描画する特定の矩形を指定する
  • contentMode — boundsが変更され、contentModeが再描画を必要とする場合(例: .redraw)
  • setNeedsLayout() — サブビューの再配置後、再描画が必要になる場合がある

Appleのドキュメントは警告しています:draw(_:) を直接呼び出さないでください。システム自身が描画を実行するタイミングを決定し、直接呼び出しは内部キャッシュメカニズムを破壊します。再描画を要求するには、常に setNeedsDisplay() または setNeedsDisplay(_:) を使用してください。

Swiftで draw(_:) をオーバーライドする方法

Swiftで draw(_:) をオーバーライドするには、グラフィックコンテキストを取得し、続けてCore Graphicsを呼び出します。描画の論理ブロックごとに個別のメソッドを作成することをお勧めします — これによりコードの可読性とテスト容易性が向上します。

swift
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(_:) を再度呼び出さないようにできます。draw(_:) 内でランダムな値、システム時刻、ネットワークリクエストを 使用しないでください

Objective-Cでの drawRect: と 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: CGRectCGRect rect
パフォーマンス同一同一

Objective-CからSwiftにプロジェクトを移行する際、メソッド名の変更は最初のタスクの一つです。Xcodeは自動変換機能を提供しますが、drawRect: は手動で draw(_:) に更新する必要があります。Apple(2024)によると、Swiftバージョンの draw(_:) が新しいプロジェクトに推奨されます。

draw(_:) での描画の最適化

draw(_:) はCPU上で実行され、最適でない実装はフレームドロップやパフォーマンス低下の原因となる可能性があります。Appleは描画を高速化するためのいくつかの実証済みのアプローチを推奨しています。

描画操作の数を最小限にする

各Core Graphics操作(move(to:), addLine(to:), strokePath)にはオーバーヘッドがあります。操作をグループ化し、複雑な形状には CGPath を使用してください — パスは一度作成され、draw(_:) の呼び出しごとに再利用されます。

ベクターオブジェクトには UIBezierPath を使用する

UIBezierPath はCGPathのObjective-Cラッパーであり、形状を作成するためのシンプルなAPIを提供します。UIBezierPathを事前に作成し(例:初期化子で)、draw(_:) 内で単に fill() または stroke() を呼び出します。

swift
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(_:) を使用する際のよくある間違い

開発者は draw(_:) をオーバーライドする際に同じ間違いをよく犯します。これらのパターンを認識することで、バグやパフォーマンスの低下を回避できます。最も一般的な問題とその解決策を見てみましょう。

  • draw(_:) の直接呼び出し — draw(_:) を直接呼び出さないでください。再描画を要求するには setNeedsDisplay() を使用してください。直接呼び出しはキャッシュを破壊し、不正な表示を引き起こす可能性があります。
  • draw(_:) 内での重い計算 — draw(_:) は可能な限り軽量であるべきです。UIBezierPath、画像、その他の重いオブジェクトはメソッドの外部で作成し、一度だけ初期化してください。
  • draw(_:) 内でのオブジェクト作成 — draw(_:) 内でのUIColor、UIFont、UIGraphicsImageRendererの構築は、不必要なメモリ負荷を生み出します。オブジェクトの作成はクラスプロパティに移動してください。
  • rectパラメータの無視 — rectは再描画が必要な領域を指定します。rectの外側の描画はシステムによって破棄されますが、リソースを消費します。描画前にrectとの交差を確認してください。
  • super.draw(rect) の欠如 — UIViewの実装は空ですが、Appleは将来のUIKit変更との互換性のために super.draw(rect) を呼び出すことを推奨しています。

これらのルールに従うことで、どのiOSプロジェクトでも安定した高速なカスタムビュー描画を確保できます。実際の実行時間とボトルネックを確認するには、Instruments(Core Animation)を使用して draw(_:) を プロファイリング してください。

よくある質問

draw(_:) を直接呼び出すことはできますか?

いいえ、draw(_:) の直接呼び出しはAppleのドキュメントで禁止されています。システム自身が描画サイクルを管理します。再描画を要求するには、setNeedsDisplay() を使用してください。これは最も近いレンダリングサイクルでビューを更新が必要として正しくマークします。

drawRect: と draw(_:) の違いは何ですか?

機能的にはこれらのメソッドは同一です。drawRect: はObjective-Cで使用され、draw(_:) はSwiftで使用されます。どちらも再描画する領域のCGRectを受け取り、UIGraphicsGetCurrentContext() を介して同じCore Graphicsコンテキストを使用します。

空のUIViewで draw(_:) が呼び出されないのはなぜですか?

Appleはレンダリングを最適化しています:UIViewにオーバーライドされた draw(_:) がない場合、システムはそのためのビットマップコンテキストを作成しません。これによりメモリを節約できます。オーバーライドが存在するのにメソッドが呼び出されない場合は、ビューのフレームがゼロでなく、ビューが表示可能であることを確認してください。

システムはどのくらいの頻度で draw(_:) を呼び出しますか?

必要な場合のみ:最初のレンダリング時、setNeedsDisplay() 後、contentMode = .redraw でboundsが変更された時。静的な状態では、draw(_:) は再度呼び出されず、CPUとバッテリーリソースを節約します。

Swiftで super.draw(rect) を呼び出す必要はありますか?

Appleはオーバーライドされたメソッドの先頭で super.draw(rect) を呼び出すことを推奨しています。現在のUIViewの実装は空ですが、super呼び出しは将来のUIKitバージョンとの互換性を保証し、良い習慣です。

まとめ

  • draw(_:) / drawRect — Swift と Objective-C での UIView の主要な描画メソッド、システムによって自動的に呼び出される
  • CGContext — UIGraphicsGetCurrentContext() を介して draw(_:) 内で利用可能な Core Graphics コンテキスト
  • setNeedsDisplay() — 再描画を要求する正しい方法;draw(_:) の直接呼び出しは禁止
  • 冪等性 — 適切なキャッシュのために、draw(_:) は同じ入力データで同じ結果を生成する必要がある
  • UIBezierPath — 各呼び出しでのCPU負荷を最小限にするために、draw(_:) の外部でパスを作成する
  • rectパラメータ — 再描画領域を含む;最適化のために交差を使用 — その外側では描画しない
  • プロファイリング — Instruments Core Animation を使用して draw(_:) のパフォーマンスを確認し、低速な操作を特定する

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください