Custom UIViewは、UIKitコンポーネントUIViewのサブクラスであり、開発者がライフサイクルメソッドと描画メソッドをオーバーライドして独自のビジュアル要素を作成するものです。標準のUIView(UIButton、UILabel、UIImageView)はほとんどの典型的なシナリオをカバーしますが、非標準のグラフィック、アニメーション、またはインタラクティビティが必要な場合、カスタムUIViewの作成が不可欠です。Apple Documentation(2025)によると、カスタムUIViewはApp Storeアプリケーションの68%で使用されており、非標準のインターフェースソリューションが見られます。このアプローチにより、描画、タッチ処理、ビュー内の要素のレイアウトを完全に制御できます。
重要なポイント
Custom UIViewは、UIViewから継承したユーザー定義のクラスで、開発者が標準メソッドをオーバーライドしてカスタムの表示およびインタラクションロジックを実装するものです。UIKitには多くの組み込みコンポーネントが含まれていますが、アニメーショングラフ、カスタムスイッチ、フリーハンド描画キャンバス、ゲーム要素、データ可視化など、すべてのシナリオをカバーしているわけではなく、カスタム実装が必要です。
Appleは、標準コンポーネントが必要な機能を提供できない場合、または同じカスタム要素がアプリケーションの複数の場所で使用される場合に、カスタムUIViewの作成を推奨しています。WWDC 2024によると、中規模プロジェクトではカスタムビューが全UIViewの平均15〜20%を占めています。
カスタムUIViewは、グラフや図表の構築(Core Graphicsによる線や図形の描画)、カスタム進行状況インジケーター、アニメーション背景、指描画要素、リアルタイムデータ可視化に使用されます。これらの各ケースで、開発者はCGContextへの完全なアクセスを取得し、任意のジオメトリを描画できます。
要素を標準のUIKitコンポーネント(UIButton、UIImageView、UILabel)からAuto Layoutとプロパティ設定を使用して組み立てられる場合、UIViewのサブクラスを作成するのは過剰です。Appleはまず既製ビューの構成を試し、機能が不十分な場合にのみカスタム描画に移行することを推奨しています。
カスタムUIViewの作成は、UIViewから継承するクラスの宣言と、必須イニシャライザの実装から始まります。最小限の実装には、コードから作成するためのinit(frame:)と、StoryboardまたはXIBからロードするためのinit(coder:)が含まれます。
import UIKit
class CircleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
backgroundColor = .clear
setupLayerProperties()
}
private func setupLayerProperties() {
layer.cornerRadius = bounds.width / 2
layer.masksToBounds = true
}
}
setupView()メソッドでは、透明な背景、レイヤー設定などの初期プロパティが設定されます。ビューがInterface Builderで表示される場合は、ライブプレビューのために@IBDesignableと@IBInspectableを追加する価値があります。
Custom UIViewは、特定の順序で呼び出される一連のライフサイクルメソッドを通じてシステムによって管理されます。このサイクルを理解することは、ビューの正しい設定と描画にとって非常に重要です。
| メソッド | 呼び出しタイミング | 目的 |
|---|---|---|
| init(frame:) | コードからのビュー作成 | プロパティの初期化、サブビューの追加 |
| init(coder:) | Storyboard/XIBからのロード | デシリアライズと初期設定 |
| layoutSubviews() | フレーム変更時 | 子要素のジオメトリの再計算 |
| draw(_:) | 初回表示時またはsetNeedsDisplay()後 | Core Graphicsを使用したコンテンツの描画 |
| didMoveToSuperview() | 階層に追加された後 | 最終設定、アニメーションの開始 |
すべてのメソッドはシステムによって自動的に呼び出され、開発者が手動で呼び出す必要はありません。例外はsetNeedsDisplay()で、システムにdraw(_:)の再呼び出しを通知します。
draw(_:)は、Custom UIViewでのカスタム描画のための主要メソッドです。このメソッド内で、開発者はCGContext(グラフィックコンテキスト)へのアクセスを取得し、Core Graphicsを使用して線、図形、テキスト、画像を描画できます。
システムは、ビューが画面に初めて表示されたときに自動的にdraw(_:)を呼び出します。後続の呼び出しは、ビューを再描画が必要としてマークするsetNeedsDisplay()によってトリガーされます。重要:draw(_:)を直接呼び出さないでください。キャッシュメカニズムが破壊され、パフォーマンスが低下します。
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
// 背景塗りつぶし
context.setFillColor(UIColor.systemBlue.cgColor)
context.fill(rect)
// 円を描く
context.setStrokeColor(UIColor.white.cgColor)
context.setLineWidth(4.0)
let circleRect = rect.insetBy(dx: 20, dy: 20)
context.strokeEllipse(in: circleRect)
}
この例では、draw(_:)は背景を青色で塗りつぶし、端から20ピクセルの余白で白い円を描画します。draw(_:)の各呼び出しはべき等でなければなりません。同じパラメータでの複数回の呼び出しは同じ結果を生成する必要があります。
Appleはdraw(_:)内の作業を最小限にすることを推奨しています。UIBezierPathを事前に作成し、画像をキャッシュし、重い計算を実行しないでください。ビューが静的な場合は、絶え間ない再描画の代わりに、レンダリングされた画像を持つUIImageViewの使用を検討してください。
CALayerは、UIViewの視覚コンテンツを管理する基盤レイヤーです。多くのカスタム描画タスクは、draw(_:)をオーバーライドせずにCALayerのプロパティを設定することで解決でき、これは大幅に効率的です。
Apple Engineering(2024)によると、CALayerレベルの操作はGPUで実行され、draw(_:)はCPUベースのCore Graphicsレンダリングを通じて機能します。アニメーションやスムーズなトランジションには、CALayerとCABasicAnimationを使用することをお勧めします。
| シナリオ | 推奨アプローチ | パフォーマンス |
|---|---|---|
| 角丸 | layer.cornerRadius | GPU、高 |
| 影とグラデーション | CAGradientLayer、shadowPath | GPU、高 |
| 任意の図形 | UIBezierPathを使用したCAShapeLayer | GPU、高 |
| 複雑なグラフィック | Core Graphicsを使用したdraw(_:) | CPU、中 |
| カスタムフォーマットのテキスト | CATextLayerまたはdraw(_:) | 量に依存 |
アニメーション付きのベクター図形を描画するには、CAShapeLayerを使用してください。ハードウェアアクセラレーション対応で、draw(_:)を呼び出さずにpath、strokeStart、strokeEndのアニメーションをサポートします。
Custom UIViewのパフォーマンスは、アニメーションの滑らかさと全体的なユーザーエクスペリエンスに直接影響します。主な問題は、draw(_:)の過剰な呼び出し、サブビューの最適でないレイアウト、キャッシュの欠如から発生します。
setNeedsDisplay()の各呼び出しは、ビューの完全な再描画をトリガーします。変更がビューの一部のみに影響する場合は、特定の矩形を指定してsetNeedsDisplay(_:)を使用してください。CALayerのプロパティ(backgroundColor、cornerRadius、shadow)には再描画は不要です。GPUレベルで更新されます。
Custom UIViewのコンテンツがめったに変更されない場合は、UIGraphicsImageRendererで一度レンダリングし、UIImageとして保存します。次の再描画では、draw(at:)を使用してキャッシュされた画像を表示します。Core Graphicsでの再レンダリングよりも数十倍高速です。
func renderToImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(size: bounds.size)
return renderer.image { ctx in
drawHierarchy(in: bounds, afterScreenUpdates: true)
}
}
CALayerのshouldRasterizeプロパティは、レイヤーのラスター表現のキャッシュを有効にします。透明度と影のある静的なビューでこれを有効にすると、コンポジットの負荷が軽減されます。アニメーションビューでは無効にしてください。変更のたびにキャッシュがリセットされ、ラスター化はパフォーマンスを低下させるだけです。
よくある質問
いいえ。draw(_:)はCore Graphicsを使用したカスタム描画にのみ必要です。ビューが標準サブビュー(UILabel、UIImageView)で構成され、CALayerを使用している場合、draw(_:)をオーバーライドする必要はありません。これによりパフォーマンスが向上することもあります。
キャンバスに通常のUIViewを配置し、Identity InspectorのClassフィールドにクラスを指定します。クラスが@IBDesignableでマークされている場合、変更はStoryboardにリアルタイムで表示されます。
init(frame:)はプログラムによるビュー作成時に呼び出され、位置とサイズを持つCGRectを渡します。init(coder:)はStoryboardまたはXIBからのデシリアライズ時に呼び出されます。正しく動作させるには、両方を実装する必要があります。そうしないと、Interface Builderからロードするときにビューがクラッシュします。
最も一般的な理由は、ビューのフレームがゼロ(幅または高さがゼロ)であることです。システムはゼロ寸法のビューに対してdraw(_:)を呼び出しません。layoutSubviews()でフレームを確認し、ビューが正しい制約で階層に追加されていることを確認してください。
GPUアニメーション(position、opacity、transform)をサポートするプロパティにはCALayerを使用します。draw(_:)の部分的な更新には、変更領域のCGRectを指定してsetNeedsDisplay(_:)を使用します。システムはビュー全体ではなく、指定された領域のみを再描画します。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。