Les vues personnalisées sont la base de l'interface unique de toute application mobile. Les vues personnalisées dans les applications mobiles permettent de créer vos propres graphiques, animations et éléments non disponibles dans les composants standard. Selon Apple Developer, 2025, drawRect reste la méthode principale de dessin personnalisé dans le développement mobile iOS. Sur Android, onDraw avec Canvas joue le même rôle, et sur Flutter — CustomPainter.
L'essentiel
Les vues personnalisées sont des composants d'interface définis par l'utilisateur qui redéfinissent les méthodes standard de dessin et de mesure pour créer des graphiques uniques. Graphiques, animations, diagrammes, éléments de chat personnalisés et masques AR — voici des scénarios typiques d'utilisation des vues personnalisées dans les applications mobiles. Les composants d'interface standard (Button, TextView, Label) couvrent 80% des tâches, mais les 20% restants nécessitent un contrôle total sur le dessin dans le développement mobile. Le dessin dans le développement mobile est une compétence qui distingue un développeur junior d'un senior.
Les vues personnalisées dans les applications mobiles sont nécessaires pour des graphiques 2D uniques, des animations complexes, des diagrammes et la visualisation de données. Si l'UIKit standard ne fournit pas l'élément requis — une vue personnalisée est créée. Dans les applications mobiles, les vues personnalisées sont utilisées pour les barres de progression, les boutons personnalisés, les remplissages dégradés, les icônes animées et les toiles de dessin au doigt. Il est important de ne pas en abuser — pour les styles simples, il suffit de configurer les composants standard via l'API d'apparence.
Sur iOS, les vues personnalisées sont créées en héritant de UIView et en redéfinissant la méthode drawRect. drawRect est appelé au premier rendu et après chaque appel à setNeedsDisplay(). À l'intérieur de drawRect, le développeur obtient un CGContext via UIGraphicsGetCurrentContext() — une toile pour les lignes, les formes, les dégradés et le texte. Core Graphics (Quartz 2D) est un moteur de rendu bas niveau sur CPU qui offre un contrôle total sur chaque pixel. UIBezierPath est un wrapper pratique pour créer des chemins avec une syntaxe concise.
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 fournit CGContext — une toile pour toutes les opérations de dessin. CGGradient crée des dégradés linéaires et radiaux avec prise en charge de CGColorSpace (sRGB, Display P3). Les transformations sont effectuées via CGAffineTransform — une matrice 3x3 pour la rotation, la mise à l'échelle et la translation. CGContextTranslateCTM, CGContextRotateCTM et CGContextScaleCTM modifient le système de coordonnées. Core Graphics fonctionne sur CPU, donc des graphiques complexes peuvent charger le processeur — utilisez Metal pour l'accélération GPU.
UIBezierPath est un wrapper orienté objet autour de CGPath. La méthode addArc(withCenter:radius:startAngle:endAngle:clockwise:) crée des arcs et des cercles, addCurve crée des courbes de Bézier. Les chemins prennent en charge les opérations d'union, d'intersection et de soustraction via addClip. UIBezierPath est pratique pour les masques personnalisés (maskView), les chemins de détourage et les formes complexes — étoiles, cœurs, icônes personnalisées.
Sur Android, une vue personnalisée étend View avec onDraw(Canvas) redéfini. Canvas contient toutes les méthodes de dessin dans le développement mobile : drawCircle, drawRect, drawLine, drawText, drawBitmap, drawPath et drawRoundRect. Paint configure les paramètres — couleur, épaisseur de ligne, style (FILL, STROKE, FILL_AND_STROKE), anti-crénelage (isAntiAlias) et taille du texte. onDraw est appelé à chaque invalidate() et ne doit pas contenir d'opérations lourdes — tous les calculs sont mis en cache.
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)
}
}
Path sur Android est l'équivalent d'UIBezierPath : addCircle, addRect, addOval, lineTo, cubicTo et quadTo. PathMeasure calcule la longueur du chemin et les coordonnées à n'importe quel point — utile pour animer le mouvement d'un objet le long d'un chemin. Pour les animations, utilisez ValueAnimator avec invalidate() dans le callback. L'animateur peut changer la couleur, le rayon, la position — toute propriété affectant le dessin. Les vues personnalisées sur Android prennent également en charge StateListDrawable et animated-vector pour l'animation via XML.
Les dégradés sur Android sont définis via Shader — LinearGradient, RadialGradient et SweepGradient. Shader est assigné à Paint via paint.setShader(shader). LinearGradient génère un dégradé linéaire entre deux points avec des positions de couleur optionnelles. RadialGradient — radial du centre vers les bords. SweepGradient — conique autour d'un point central. Shader.TileMode (CLAMP, REPEAT, MIRROR) définit le comportement au-delà des limites de la zone.
Flutter utilise CustomPainter — une classe avec les méthodes paint(Canvas canvas, Size size) et shouldRepaint. Canvas dans Flutter fournit les mêmes méthodes que dans Android : drawCircle, drawRect, drawLine, drawPath, drawParagraph et drawImage. Paint dans Flutter configure la couleur, l'épaisseur, le style et l'anti-crénelage. Flutter n'utilise ni drawRect ni onDraw — tout le dessin passe par CustomPainter, intégré dans le widget CustomPaint.
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 détermine si le CustomPainter doit être redessiné. Ne renvoyez true que lorsque les données affectant le rendu changent — couleur, taille, données du graphique. RepaintBoundary isole le redessin dans une couche séparée — utilisez-le pour les widgets qui changent rarement. Flutter n'a pas de setNeedsDisplay ni d'invalidate — le redessin est déclenché via setState ou ValueNotifier avec écoute des changements.
Flutter prend en charge LinearGradient, RadialGradient et SweepGradient — les mêmes types que dans Android. Gradient crée un Shader pour Paint via createShader(Rect bounds). Les transformations sont effectuées via canvas.rotate, canvas.scale, canvas.translate et canvas.skew. Flutter utilise une matrice de transformation 4x4 (Matrix4) pour le widget Transform. Les animations de dessin dans Flutter se font avec AnimationController combiné à setState pour redessiner CustomPainter.
La mesure correcte des vues personnalisées est la base d'un comportement prévisible de l'interface. Sur iOS, la taille de la View est déterminée via sizeThatFits et intrinsicContentSize. sizeThatFits renvoie la taille optimale pour les contraintes données. intrinsicContentSize — la taille naturelle du contenu sans contraintes externes. Sur Android, la mesure est implémentée dans onMeasure avec les paramètres MeasureSpec (UNSPECIFIED, EXACTLY, AT_MOST). Dans Flutter, la taille est passée à paint(Canvas, Size) et peut être calculée via computeDryLayout ou LayoutBuilder.
intrinsicContentSize renvoie un CGSize — la taille naturelle de la View basée sur son contenu. setContentHuggingPriority et setContentCompressionResistancePriority contrôlent le comportement lors de l'étirement et de la compression. sizeThatFits est appelé par le système pour calculer la taille dans Auto Layout. Pour les vues personnalisées, redéfinissez intrinsicContentSize si votre composant a une taille naturelle — par exemple, un cercle doit toujours être 100x100.
onMeasure accepte widthMeasureSpec et heightMeasureSpec. MeasureSpec contient le mode et la taille : UNSPECIFIED (sans contraintes, la View choisit sa taille), EXACTLY (taille exacte du parent), AT_MOST (taille maximale). setMeasuredDimension(width, height) sauvegarde le résultat de la mesure. Lors du changement de données, appelez requestLayout() pour remesurer. Les ViewGroups imbriqués nécessitent la redéfinition de onLayout pour disposer les éléments enfants.
| Plateforme | Méthode de mesure | Appel lors du changement | Retour |
|---|---|---|---|
| iOS | sizeThatFits / intrinsicContentSize | setNeedsLayout / invalidateIntrinsicContentSize | CGSize |
| Android | onMeasure / setMeasuredDimension | requestLayout / invalidate | void (sauvegarde la taille) |
| Flutter | computeDryLayout | setState / LayoutBuilder | Size |
computeDryLayout est une méthode RenderObject pour calculer la taille sans cycle de layout complet. LayoutBuilder.getSize — une alternative pour obtenir l'espace disponible dans la méthode build. Dans Flutter, la taille de la toile est passée à paint comme Size et ne nécessite pas de mesure séparée — CustomPainter dessine dans les limites passées. Cependant, pour les RenderObjects personnalisés, vous devez redéfinir performLayout et computeDryLayout.
Foire aux questions
drawRect est une méthode UIView pour le dessin personnalisé via Core Graphics. Elle est appelée au premier rendu de la View et après chaque appel à setNeedsDisplay(). Utilisez CGContext dans drawRect pour les lignes, les formes et le texte.
Créez une classe héritant de View, redéfinissez onDraw(Canvas canvas) et utilisez l'API Canvas : drawCircle, drawRect, drawPath. Paint configure la couleur, l'épaisseur et le style. Pour mettre à jour, appelez invalidate().
CustomPainter est une classe Flutter pour le dessin personnalisé sur Canvas. Implémentez les méthodes paint(Canvas, Size) et shouldRepaint. Il est placé via le widget CustomPaint. Flutter n'a pas de drawRect — tout le dessin passe par CustomPainter.
Sur iOS utilisez sizeThatFits et intrinsicContentSize. Sur Android — onMeasure avec MeasureSpec. Dans Flutter, la taille est passée à paint(Canvas, Size) ou calculée via computeDryLayout. Chaque plateforme utilise sa propre méthode de mesure.
Les vues personnalisées sont nécessaires pour des graphiques uniques, des animations, des diagrammes et des éléments qui ne peuvent pas être implémentés avec UIKit ou View standard. Pour les styles simples, il suffit de configurer les composants standard — ne compliquez pas l'architecture inutilement.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.