カスタムビューは、あらゆるモバイルアプリのユニークなインターフェースの基盤です。モバイルアプリのカスタムビューを使用すると、標準コンポーネントでは利用できない独自のグラフィック、アニメーション、要素を作成できます。Apple Developer、2025によると、drawRectはiOSモバイル開発におけるカスタム描画の主要メソッドであり続けています。AndroidではCanvasを伴うonDrawが同じ役割を果たし、FlutterではCustomPainterがその役割を担います。
重要なポイント
カスタムビューは、独自のグラフィックを作成するために標準の描画および測定メソッドをオーバーライドするユーザー定義のインターフェースコンポーネントです。グラフ、アニメーション、図表、カスタムチャット要素、ARマスク — これらはモバイルアプリにおけるカスタムビューの典型的な使用例です。標準のUIコンポーネント(Button、TextView、Label)は80%のタスクをカバーしますが、残りの20%はモバイル開発における描画の完全な制御を必要とします。モバイル開発における描画は、ジュニア開発者とシニア開発者を区別するスキルです。
モバイルアプリのカスタムビューは、ユニークな2Dグラフィック、複雑なアニメーション、チャート、データ可視化のために必要です。標準のUIKitが必要な要素を提供しない場合 — カスタムビューが作成されます。モバイルアプリでは、カスタムビューはプログレスバー、カスタムボタン、グラデーション塗りつぶし、アニメーションアイコン、指での描画キャンバスに使用されます。過度に使用しないことが重要です — シンプルなスタイルには、Appearance APIを介して標準コンポーネントを設定するだけで十分です。
iOSでは、UIViewを継承してdrawRectメソッドをオーバーライドすることでカスタムビューを作成します。drawRectは最初のレンダリング時と、setNeedsDisplay()の呼び出しごとに呼び出されます。drawRect内で、開発者はUIGraphicsGetCurrentContext()を介してCGContextを取得します — 線、図形、グラデーション、テキストのためのキャンバスです。Core Graphics(Quartz 2D)は、すべてのピクセルを完全に制御できる低レベルのCPUベースのレンダリングエンジンです。UIBezierPathは、簡潔な構文でパスを作成するための便利なラッパーです。
class CircleView: UIView {
private var fillColor: UIColor = .systemBlue
override func drawRect(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.setFillColor(fillColor.cgColor)
ctx.addEllipse(in: bounds.insetBy(dx: 4, dy: 4))
ctx.drawPath(using: .fill)
}
func updateColor(_ newColor: UIColor) {
fillColor = newColor
setNeedsDisplay()
}
}
Core Graphicsは、すべての描画操作のためのキャンバスであるCGContextを提供します。CGGradientは、CGColorSpace(sRGB、Display P3)をサポートして線形および放射状のグラデーションを作成します。変換はCGAffineTransformを介して実行されます — 回転、スケーリング、移動のための3x3マトリックスです。CGContextTranslateCTM、CGContextRotateCTM、CGContextScaleCTMは座標系を変更します。Core GraphicsはCPUで動作するため、複雑なグラフィックはプロセッサに負荷をかける可能性があります — GPUアクセラレーションにはMetalを使用してください。
UIBezierPathはCGPath上のオブジェクト指向ラッパーです。addArc(withCenter:radius:startAngle:endAngle:clockwise:)メソッドは円弧と円を作成し、addCurveはベジェ曲線を作成します。パスはaddClipを介して結合、交差、減算操作をサポートします。UIBezierPathは、カスタムマスク(maskView)、クリッピングパス、複雑な図形 — 星、ハート、カスタムアイコンに便利です。
Androidでは、カスタムビューはonDraw(Canvas)をオーバーライドしてViewを拡張します。Canvasにはモバイル開発におけるすべての描画メソッドが含まれています:drawCircle、drawRect、drawLine、drawText、drawBitmap、drawPath、drawRoundRect。Paintはパラメータを設定します — 色、線の太さ、スタイル(FILL、STROKE、FILL_AND_STROKE)、アンチエイリアス(isAntiAlias)、テキストサイズ。onDrawはinvalidate()のたびに呼び出され、重い操作を含めてはいけません — すべての計算はキャッシュされます。
class CustomCircleView(context: Context, attrs: AttributeSet?)
: View(context, attrs) {
private val paint = Paint().apply {
style = Paint.Style.FILL
color = Color.BLUE
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = minOf(cx, cy) - 4f
canvas.drawCircle(cx, cy, radius, paint)
}
}
AndroidのPathはUIBezierPathに相当します:addCircle、addRect、addOval、lineTo、cubicTo、quadTo。PathMeasureはパスの長さと任意のポイントの座標を計算します — パスに沿ったオブジェクトの動きをアニメーション化するのに便利です。アニメーションには、コールバックでinvalidate()を使用するValueAnimatorを使用します。アニメーターは色、半径、位置を変更できます — 描画に影響を与える任意のプロパティです。Androidのカスタムビューは、XMLを介したアニメーションのためのStateListDrawableとanimated-vectorもサポートしています。
AndroidのグラデーションはShaderを介して設定されます — LinearGradient、RadialGradient、SweepGradient。Shaderはpaint.setShader(shader)を介してPaintに割り当てられます。LinearGradientは、2点間のオプションの色位置を持つ線形グラデーションを生成します。RadialGradient — 中心から端への放射状。SweepGradient — 中心点の周りの円錐状。Shader.TileMode(CLAMP、REPEAT、MIRROR)は領域の境界を超えた動作を定義します。
FlutterはCustomPainterを使用します — paint(Canvas canvas, Size size)メソッドとshouldRepaintメソッドを持つクラスです。FlutterのCanvasはAndroidと同じメソッドを提供します:drawCircle、drawRect、drawLine、drawPath、drawParagraph、drawImage。FlutterのPaintは色、太さ、スタイル、アンチエイリアスを設定します。FlutterはdrawRectやonDrawを使用しません — すべての描画はCustomPaintウィジェットに組み込まれたCustomPainterを介して行われます。
class CirclePainter extends CustomPainter {
final Color color;
CirclePainter({required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill
..isAntiAlias = true;
final radius = size.shortestSide / 2 - 4;
canvas.drawCircle(size.center(Offset.zero), radius, paint);
}
@override
bool shouldRepaint(CirclePainter oldDelegate) =>
oldDelegate.color != color;
}
shouldRepaintは、CustomPainterを再描画する必要があるかどうかを決定します。レンダリングに影響を与えるデータが変更された場合のみtrueを返します — 色、サイズ、チャートデータ。RepaintBoundaryは再描画を個別のレイヤーに分離します — めったに変更されないウィジェットに使用します。FlutterにはsetNeedsDisplayやinvalidateはありません — 再描画はsetStateまたは変更をリッスンするValueNotifierを介してトリガーされます。
FlutterはLinearGradient、RadialGradient、SweepGradientをサポートしています — Androidと同じタイプです。GradientはcreateShader(Rect bounds)を介してPaintのShaderを作成します。変換はcanvas.rotate、canvas.scale、canvas.translate、canvas.skewを介して実行されます。FlutterはTransformウィジェットに4x4変換マトリックス(Matrix4)を使用します。Flutterの描画アニメーションは、CustomPainterを再描画するためにsetStateと組み合わせてAnimationControllerを使用して行われます。
カスタムビューの正確な測定は、予測可能なインターフェース動作の基盤です。iOSでは、ビューのサイズはsizeThatFitsとintrinsicContentSizeを介して決定されます。sizeThatFitsは、指定された制約に対する最適なサイズを返します。intrinsicContentSize — 外部制約のないコンテンツの自然なサイズ。Androidでは、測定はMeasureSpecパラメータ(UNSPECIFIED、EXACTLY、AT_MOST)を使用してonMeasureで実装されます。Flutterでは、サイズはpaint(Canvas, Size)に渡され、computeDryLayoutまたはLayoutBuilderを介して計算できます。
intrinsicContentSizeはCGSizeを返します — そのコンテンツに基づくビューの自然なサイズです。setContentHuggingPriorityとsetContentCompressionResistancePriorityは、伸長と圧縮時の動作を制御します。sizeThatFitsは、Auto Layoutでサイズを計算するためにシステムによって呼び出されます。カスタムビューの場合、コンポーネントに自然なサイズがある場合はintrinsicContentSizeをオーバーライドします — たとえば、円は常に100x100である必要があります。
onMeasureはwidthMeasureSpecとheightMeasureSpecを受け入れます。MeasureSpecにはモードとサイズが含まれています:UNSPECIFIED(制約なし、ビューが自身のサイズを選択)、EXACTLY(親からの正確なサイズ)、AT_MOST(最大サイズ)。setMeasuredDimension(width, height)は測定結果を保存します。データが変更されたら、再測定のためにrequestLayout()を呼び出します。ネストされたViewGroupは、子要素を配置するためにonLayoutのオーバーライドが必要です。
| プラットフォーム | 測定メソッド | 変更時の呼び出し | 戻り値 |
|---|---|---|---|
| iOS | sizeThatFits / intrinsicContentSize | setNeedsLayout / invalidateIntrinsicContentSize | CGSize |
| Android | onMeasure / setMeasuredDimension | requestLayout / invalidate | void(サイズを保存) |
| Flutter | computeDryLayout | setState / LayoutBuilder | Size |
computeDryLayoutは、完全なレイアウトサイクルなしでサイズを計算するためのRenderObjectメソッドです。LayoutBuilder.getSize — buildメソッドで利用可能なスペースを取得するための代替手段です。Flutterでは、キャンバスのサイズはSizeとしてpaintに渡され、個別の測定は必要ありません — CustomPainterは渡された境界内で描画します。ただし、カスタムRenderObjectの場合は、performLayoutとcomputeDryLayoutをオーバーライドする必要があります。
よくある質問
drawRectは、Core Graphicsを介したカスタム描画のためのUIViewメソッドです。ビューの最初のレンダリング時と、setNeedsDisplay()の呼び出しごとに呼び出されます。線、図形、テキストについてはdrawRect内でCGContextを使用します。
Viewを拡張するクラスを作成し、onDraw(Canvas canvas)をオーバーライドしてCanvas APIを使用します:drawCircle、drawRect、drawPath。Paintは色、太さ、スタイルを設定します。更新するにはinvalidate()を呼び出します。
CustomPainterは、Canvasへのカスタム描画のためのFlutterクラスです。paint(Canvas, Size)メソッドとshouldRepaintメソッドを実装します。CustomPaintウィジェットを介して配置されます。FlutterにはdrawRectはありません — すべての描画はCustomPainterを介して行われます。
iOSではsizeThatFitsとintrinsicContentSizeを使用します。Androidでは — MeasureSpecを伴うonMeasure。Flutterでは、サイズはpaint(Canvas, Size)に渡されるか、computeDryLayoutを介して計算されます。各プラットフォームは独自の測定メソッドを使用します。
カスタムビューは、標準のUIKitやViewでは実装できないユニークなグラフィック、アニメーション、チャート、要素に必要です。シンプルなスタイルの場合は、標準コンポーネントを設定するだけで十分です — 不必要にアーキテクチャを複雑にしないでください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。