العروض المخصصة والرسم في التطوير المحمول: الجوهر، واجهات API، وكيف يعمل

المؤلف: IT Sectr نُشر: 2026-08-01 وقت القراءة: 11 دق

العروض المخصصة هي أساس الواجهة الفريدة لأي تطبيق محمول. تتيح العروض المخصصة في التطبيقات المحمولة إنشاء رسومات ورسوم متحركة وعناصر خاصة غير متوفرة في المكونات القياسية. وفقًا لـ Apple Developer، 2025، يظل drawRect الطريقة الأساسية للرسم المخصص في تطوير iOS المحمول. على Android، يؤدي onDraw مع Canvas نفس الدور، بينما يستخدم Flutter CustomPainter.

أهم النقاط

  • العروض المخصصة — مكونات يحددها المستخدم بمنطق رسم وحجم وسلوك خاص بها في التطبيقات المحمولة.
  • drawRect — طريقة UIView على iOS للرسم المخصص عبر Core Graphics و CGContext.
  • onDraw — طريقة View على Android للرسم عبر Canvas و Paint مع نظام القياس onMeasure.
  • CustomPainter — فئة Flutter للرسم على Canvas بطريقتي paint و shouldRepaint.
  • القياس — عملية تحديد أبعاد العرض عبر sizeThatFits على iOS و onMeasure على Android و computeDryLayout في Flutter.

ما هي العروض المخصصة ولماذا نحتاجها في التطبيقات المحمولة

العروض المخصصة هي مكونات واجهة يحددها المستخدم تعيد تعريف طرق الرسم والقياس القياسية لإنشاء رسومات فريدة. الرسوم البيانية والرسوم المتحركة والمخططات وعناصر الدردشة المخصصة وأقنعة AR — هذه سيناريوهات نموذجية لاستخدام العروض المخصصة في التطبيقات المحمولة. تغطي مكونات واجهة المستخدم القياسية (Button و TextView و Label) 80% من المهام، لكن الـ 20% المتبقية تتطلب تحكمًا كاملاً في الرسم في التطوير المحمول. الرسم في التطوير المحمول هو مهارة تميز المطور المبتدئ عن الخبير.

متى تكون العروض المخصصة مبررة

العروض المخصصة في التطبيقات المحمولة ضرورية لـ الرسومات ثنائية الأبعاد الفريدة والرسوم المتحركة المعقدة والمخططات وتصور البيانات. إذا كان UIKit القياسي لا يوفر العنصر المطلوب — يتم إنشاء عرض مخصص. في التطبيقات المحمولة، تُستخدم العروض المخصصة لأشرطة التقدم والأزرار المخصصة والتعبئات المتدرجة والأيقونات المتحركة ولوحات الرسم بالأصابع. من المهم عدم الإفراط في الاستخدام — للأنماط البسيطة، يكفي تكوين المكونات القياسية عبر Appearance API.

العروض المخصصة على iOS: drawRect و Core Graphics

على iOS، يتم إنشاء العروض المخصصة عن طريق وراثة UIView وإعادة تعريف طريقة drawRect. يتم استدعاء drawRect عند العرض الأول وبعد كل استدعاء setNeedsDisplay(). داخل drawRect، يحصل المطور على CGContext عبر UIGraphicsGetCurrentContext() — لوحة للخطوط والأشكال والتدرجات والنص. Core Graphics (Quartz 2D) هو محرك عرض منخفض المستوى على CPU يمنح تحكمًا كاملاً في كل بكسل. UIBezierPath هو غلاف مناسب لإنشاء المسارات ببناء جملة موجز.

swift
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 والتحويلات

يوفر Core Graphics CGContext — لوحة لجميع عمليات الرسم. يقوم CGGradient بإنشاء تدرجات خطية وشعاعية مع دعم CGColorSpace (sRGB، Display P3). تتم التحويلات عبر CGAffineTransform — مصفوفة 3x3 للتدوير والقياس والإزاحة. تقوم CGContextTranslateCTM و CGContextRotateCTM و CGContextScaleCTM بتعديل نظام الإحداثيات. يعمل Core Graphics على CPU، لذا يمكن للرسومات المعقدة أن تثقل المعالج — استخدم Metal لتسريع GPU.

UIBezierPath لإنشاء الأشكال

UIBezierPath هو غلاف كائني التوجه فوق CGPath. تنشئ طريقة addArc(withCenter:radius:startAngle:endAngle:clockwise:) أقواسًا ودوائر، وتنشئ addCurve منحنيات Bézier. تدعم المسارات عمليات الاتحاد والتقاطع والطرح عبر addClip. UIBezierPath مناسب للأقنعة المخصصة (maskView) ومسارات القص والأشكال المعقدة — النجوم والقلوب والأيقونات المخصصة.

العروض المخصصة على Android: onDraw و Canvas

على Android، يرث العرض المخصص View مع إعادة تعريف onDraw(Canvas). يحتوي Canvas على جميع طرق الرسم في التطوير المحمول: drawCircle و drawRect و drawLine و drawText و drawBitmap و drawPath و drawRoundRect. يقوم Paint بتكوين المعلمات — اللون وسمك الخط والنمط (FILL و STROKE و FILL_AND_STROKE) والتنعيم (isAntiAlias) وحجم النص. يتم استدعاء onDraw في كل مرة عند invalidate() ويجب ألا يحتوي على عمليات ثقيلة — يتم تخزين جميع الحسابات مؤقتًا.

kotlin
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 والرسوم المتحركة على Android

Path على Android هو المكافئ لـ UIBezierPath: addCircle و addRect و addOval و lineTo و cubicTo و quadTo. يقوم PathMeasure بحساب طول المسار والإحداثيات في أي نقطة — مفيد لتحريك حركة كائن على طول مسار. للرسوم المتحركة، يُستخدم ValueAnimator مع invalidate() في رد النداء. يمكن للمُحرك تغيير اللون ونصف القطر والموضع — أي خاصية تؤثر على الرسم. تدعم العروض المخصصة على Android أيضًا StateListDrawable و animated-vector للرسوم المتحركة عبر XML.

Shader والتدرجات في Canvas

يتم تعيين التدرجات على Android عبر Shader — LinearGradient و RadialGradient و SweepGradient. يتم تعيين Shader إلى Paint عبر paint.setShader(shader). يقوم LinearGradient بإنشاء تدرج خطي بين نقطتين مع مواضع لون اختيارية. RadialGradient — شعاعي من المركز إلى الحواف. SweepGradient — مخروطي حول نقطة مركزية. يحدد Shader.TileMode (CLAMP و REPEAT و MIRROR) السلوك خارج حدود المنطقة.

CustomPainter في Flutter: الرسم على Canvas

يستخدم Flutter CustomPainter — فئة بطريقتي paint(Canvas canvas, Size size) و shouldRepaint. يوفر Canvas في Flutter نفس الطرق الموجودة في Android: drawCircle و drawRect و drawLine و drawPath و drawParagraph و drawImage. يقوم Paint في Flutter بتكوين اللون والسمك والنمط والتنعيم. لا يستخدم Flutter drawRect أو onDraw — كل الرسم يتم عبر CustomPainter المضمن في عنصر واجهة CustomPaint.

dart
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 و RepaintBoundary

يحدد shouldRepaint ما إذا كان CustomPainter بحاجة إلى إعادة الرسم. قم بإرجاع true فقط عندما تتغير البيانات المؤثرة على العرض — اللون والحجم وبيانات الرسم البياني. يعزل RepaintBoundary إعادة الرسم في طبقة منفصلة — استخدمه لعناصر واجهة المستخدم التي نادرًا ما تتغير. لا يحتوي Flutter على setNeedsDisplay أو invalidate — يتم تشغيل إعادة الرسم عبر setState أو ValueNotifier مع الاستماع للتغييرات.

التدرجات والتحويلات في Flutter

يدعم Flutter LinearGradient و RadialGradient و SweepGradient — نفس الأنواع الموجودة في Android. يقوم Gradient بإنشاء Shader لـ Paint عبر createShader(Rect bounds). تتم التحويلات عبر canvas.rotate و canvas.scale و canvas.translate و canvas.skew. يستخدم Flutter مصفوفة تحويل 4x4 (Matrix4) لعنصر واجهة Transform. تتم رسوم الرسم المتحركة في Flutter عبر AnimationController مع setState لإعادة رسم CustomPainter.

قياس العروض المخصصة: طرق sizeThatFits و onMeasure و computeDryLayout

القياس الصحيح للعروض المخصصة هو أساس السلوك المتوقع للواجهة. على iOS، يتم تحديد حجم العرض عبر sizeThatFits و intrinsicContentSize. يُرجع sizeThatFits الحجم الأمثل للقيود المحددة. intrinsicContentSize — الحجم الطبيعي للمحتوى دون قيود خارجية. على Android، يتم تنفيذ القياس في onMeasure مع معلمات MeasureSpec (UNSPECIFIED و EXACTLY و AT_MOST). في Flutter، يتم تمرير الحجم إلى paint(Canvas, Size) ويمكن حسابه عبر computeDryLayout أو LayoutBuilder.

sizeThatFits و intrinsicContentSize على iOS

يُرجع intrinsicContentSize CGSize — الحجم الطبيعي للعرض بناءً على محتواه. يتحكم setContentHuggingPriority و setContentCompressionResistancePriority في السلوك أثناء التمدد والضغط. يتم استدعاء sizeThatFits من قبل النظام لحساب الحجم في Auto Layout. للعروض المخصصة، أعد تعريف intrinsicContentSize إذا كان للمكون الخاص بك حجم طبيعي — على سبيل المثال، يجب أن تكون الدائرة دائمًا 100x100.

onMeasure و MeasureSpec على Android

يقبل onMeasure widthMeasureSpec و heightMeasureSpec. يحتوي MeasureSpec على الوضع والحجم: UNSPECIFIED (بدون قيود، يختار العرض حجمه بنفسه)، EXACTLY (حجم دقيق من الأصل)، AT_MOST (الحجم الأقصى). يحفظ setMeasuredDimension(width, height) نتيجة القياس. عند تغيير البيانات، استدع requestLayout() لإعادة القياس. تتطلب ViewGroup المتداخلة إعادة تعريف onLayout لترتيب العناصر الفرعية.

المنصةطريقة القياسالاستدعاء عند التغييرالإرجاع
iOSsizeThatFits / intrinsicContentSizesetNeedsLayout / invalidateIntrinsicContentSizeCGSize
AndroidonMeasure / setMeasuredDimensionrequestLayout / invalidatevoid (يحفظ الحجم)
FluttercomputeDryLayoutsetState / LayoutBuilderSize

computeDryLayout في Flutter

computeDryLayout هي طريقة RenderObject لحساب الحجم دون دورة تخطيط كاملة. LayoutBuilder.getSize — بديل للحصول على المساحة المتاحة في طريقة build. في Flutter، يتم تمرير حجم اللوحة إلى paint كـ Size ولا يتطلب قياسًا منفصلاً — يرسم CustomPainter داخل الحدود المُمررة. ومع ذلك، بالنسبة لـ RenderObject المخصصة، تحتاج إلى إعادة تعريف performLayout و computeDryLayout.

الأسئلة الشائعة

ما هو drawRect على iOS ومتى يتم استدعاؤه؟

drawRect هي طريقة UIView للرسم المخصص عبر Core Graphics. يتم استدعاؤها عند العرض الأول للعرض وبعد كل استدعاء setNeedsDisplay(). استخدم CGContext داخل drawRect للخطوط والأشكال والنص.

كيفية الرسم على Android باستخدام Canvas؟

أنشئ فئة ترث View، وأعد تعريف onDraw(Canvas canvas) واستخدم Canvas API: drawCircle و drawRect و drawPath. يقوم Paint بتكوين اللون والسمك والنمط. للتحديث، استدع invalidate().

ما هو CustomPainter في Flutter؟

CustomPainter هي فئة Flutter للرسم المخصص على Canvas. قم بتنفيذ طريقتي paint(Canvas, Size) و shouldRepaint. يتم وضعه عبر عنصر واجهة CustomPaint. لا يحتوي Flutter على drawRect — كل الرسم يتم عبر CustomPainter.

كيفية قياس حجم العرض المخصص؟

على iOS استخدم sizeThatFits و intrinsicContentSize. على Android — onMeasure مع MeasureSpec. في Flutter، يتم تمرير الحجم إلى paint(Canvas, Size) أو حسابه عبر computeDryLayout. كل منصة تستخدم طريقتها الخاصة في القياس.

متى يجب استخدام العروض المخصصة بدلاً من المكونات القياسية؟

العروض المخصصة ضرورية للرسومات الفريدة والرسوم المتحركة والمخططات والعناصر التي لا يمكن تنفيذها باستخدام UIKit أو View القياسية. للأنماط البسيطة، يكفي تكوين المكونات القياسية — لا تُعقد البنية بدون داع.

الملخص

  • العروض المخصصة — أساس الواجهات الفريدة للرسوم البيانية والرسوم المتحركة والعناصر غير القياسية في التطبيقات المحمولة.
  • drawRect — طريقة iOS للرسم عبر Core Graphics مع CGContext و UIBezierPath.
  • onDraw — طريقة Android للرسم عبر Canvas و Paint مع دعم Path و Shader.
  • CustomPainter — فئة Flutter للرسم على Canvas مع shouldRepaint و RepaintBoundary.
  • القياس — التحقق من الأحجام عبر sizeThatFits (iOS) و onMeasure (Android) و computeDryLayout (Flutter).
  • يتبع الرسم في التطوير المحمول نمطًا موحدًا عبر جميع المنصات: الحصول على سياق، تكوين الأنماط، ورسم البدائيات.
  • قم بتخزين كائنات الرسم مؤقتًا (Paint, Path) — لا تنشئها داخل drawRect أو onDraw أو paint للحصول على أداء مستقر.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع