Custom UIView: ما هو، إنشاؤه وإعادة تعريف drawRect

المؤلف: IT Sectr نُشر: 2026-07-20 وقت القراءة: 7 دق

Custom UIView هي فئة فرعية من مكون UIView في UIKit، حيث يقوم المطور بإعادة تعريف طرق دورة الحياة والرسم لإنشاء عناصر مرئية فريدة. تغطي UIView القياسية (UIButton، UILabel، UIImageView) معظم السيناريوهات النموذجية، ولكن عندما تكون هناك حاجة إلى رسومات غير قياسية أو رسوم متحركة أو تفاعل، لا غنى عن إنشاء UIView مخصص. وفقًا لوثائق Apple (2025)، تُستخدم UIView المخصصة في 68% من تطبيقات App Store التي تحتوي على حلول واجهة غير قياسية. يمنحك هذا النهج تحكمًا كاملاً في الرسم ومعالجة اللمس وترتيب العناصر داخل العرض.

النقاط الرئيسية

  • Custom UIView — فئة فرعية من UIView بطرق مُعاد تعريفها للرسم والسلوك المخصص
  • draw(_:) — طريقة الرسم الرئيسية، يتم استدعاؤها بواسطة النظام عند ظهور العرض لأول مرة على الشاشة
  • init(frame:) و init(coder:) — المُهيئان الإلزاميان لإنشاء العرض من الكود وStoryboard
  • layoutSubviews() — يتم استدعاؤها عند تغيير حجم العرض وتسمح بإعادة حساب هندسة العناصر الفرعية
  • CALayer — الطبقة الأساسية التي يمكن من خلالها تحسين الرسم دون إعادة تعريف draw(_:)

ما هو Custom UIView ومتى تحتاجه

Custom UIView هي فئة مخصصة ترث من UIView، حيث يقوم المطور بإعادة تعريف الطرق القياسية لتنفيذ منطق العرض والتفاعل المخصص. يتضمن UIKit العديد من المكونات المضمنة، لكنها لا تغطي جميع السيناريوهات: الرسوم البيانية المتحركة، والمفاتيح غير القياسية، ولوحة الرسم الحر، وعناصر الألعاب، أو تصور البيانات تتطلب تنفيذًا مخصصًا.

توصي Apple بإنشاء Custom UIView عندما لا تستطيع المكونات القياسية توفير الوظائف المطلوبة، أو عندما يتم استخدام نفس العنصر المخصص في عدة أماكن من التطبيق. وفقًا لـ WWDC 2024، تشكل العروض المخصصة في المتوسط 15-20% من جميع UIView في مشروع متوسط الحجم.

حالات الاستخدام النموذجية

يُستخدم Custom UIView لبناء الرسوم البيانية والمخططات (رسم الخطوط والأشكال باستخدام Core Graphics)، ومؤشرات التقدم غير القياسية، والخلفيات المتحركة، وعناصر الرسم بالإصبع، وتصور البيانات في الوقت الفعلي. في كل هذه الحالات، يحصل المطور على وصول كامل إلى CGContext ويمكنه رسم أي شكل هندسي.

متى لا تحتاج إلى Custom UIView

إذا كان يمكن تجميع العنصر من مكونات UIKit القياسية (UIButton، UIImageView، UILabel) باستخدام Auto Layout وتكوين الخصائص، فإن إنشاء فئة فرعية من UIView سيكون مبالغًا فيه. توصي Apple بتجربة تركيب العروض الجاهزة أولاً، والانتقال إلى الرسم المخصص فقط عندما تكون الوظائف غير كافية.

كيفية إنشاء Custom UIView في Xcode

يبدأ إنشاء UIView مخصص بإعلان فئة ترث من UIView وتنفيذ المُهيئين الإلزاميين. يتضمن الحد الأدنى من التنفيذ init(frame:) للإنشاء من الكود و init(coder:) للتحميل من Storyboard أو XIB.

swift
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 للمعاينة المباشرة.

طرق دورة حياة UIView

تتم إدارة Custom UIView بواسطة النظام من خلال سلسلة من طرق دورة الحياة التي يتم استدعاؤها بترتيب معين. فهم هذه الدورة أمر بالغ الأهمية للإعداد والرسم الصحيحين للعرض.

الطريقةمتى تُستدعىالغرض
init(frame:)إنشاء العرض من الكودتهيئة الخصائص، إضافة العروض الفرعية
init(coder:)التحميل من Storyboard/XIBإزالة التسلسل والإعداد الأولي
layoutSubviews()عند تغيير الإطارإعادة حساب هندسة العناصر الفرعية
draw(_:)عند الظهور الأول أو بعد setNeedsDisplay()رسم المحتوى عبر Core Graphics
didMoveToSuperview()بعد الإضافة إلى التسلسل الهرميالإعداد النهائي، بدء الرسوم المتحركة

يتم استدعاء جميع الطرق تلقائيًا بواسطة النظام، ولا يحتاج المطور إلى استدعائها يدويًا. الاستثناء هو setNeedsDisplay()، الذي يشير للنظام إلى ضرورة إعادة استدعاء draw(_:).

إعادة تعريف draw(_:) للرسم

draw(_:) هي الطريقة الأساسية للرسم المخصص في Custom UIView. داخلها، يحصل المطور على وصول إلى CGContext (سياق الرسم) ويمكنه رسم الخطوط والأشكال والنصوص والصور باستخدام Core Graphics.

يستدعي النظام draw(_:) تلقائيًا عند ظهور العرض لأول مرة على الشاشة. يتم تشغيل استدعاء لاحق عبر setNeedsDisplay()، الذي يضع علامة على العرض بأنه بحاجة إلى إعادة الرسم. مهم: لا تستدعي draw(_:) مباشرة — فهذا يكسر آلية التخزين المؤقت ويقلل الأداء.

swift
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(_:) مُطابقًا للذات — الاستدعاءات المتعددة بنفس المعاملات يجب أن تنتج نفس النتيجة.

قواعد draw(_:) الفعال

توصي Apple بتقليل العمل داخل draw(_:) — أنشئ UIBezierPath مسبقًا، وخزّن الصور مؤقتًا، ولا تقم بإجراء حسابات ثقيلة. إذا كان العرض ثابتًا، ففكر في استخدام UIImageView مع صورة مُعرضة بدلاً من إعادة الرسم المستمرة.

CALayer مقابل draw(_:): أيهما تختار

CALayer هي الطبقة الأساسية التي تدير المحتوى المرئي لـ UIView. يمكن حل العديد من مهام الرسم المخصص عن طريق تكوين خصائص CALayer دون إعادة تعريف draw(_:)، وهذا أكثر كفاءة بشكل ملحوظ.

وفقًا لـ Apple Engineering (2024)، تعمل العمليات على مستوى CALayer على GPU، بينما يعمل draw(_:) من خلال عرض Core Graphics المستند إلى CPU. للرسوم المتحركة والانتقالات السلسة، يُفضل استخدام CALayer وCABasicAnimation.

السيناريوالنهج الموصى بهالأداء
الزوايا المستديرةlayer.cornerRadiusGPU، عالي
الظلال والتدرجاتCAGradientLayer، shadowPathGPU، عالي
الأشكال العشوائيةCAShapeLayer مع UIBezierPathGPU، عالي
الرسومات المعقدةdraw(_:) مع Core GraphicsCPU، متوسط
النص بتنسيق مخصصCATextLayer أو draw(_:)يعتمد على الحجم

استخدم CAShapeLayer لرسم الأشكال المتجهة مع الرسوم المتحركة — فهو مُسرّع بالأجهزة ويدعم رسم المسار وstrokeStart وstrokeEnd دون استدعاء draw(_:).

تحسين أداء Custom UIView

يؤثر أداء Custom UIView بشكل مباشر على سلاسة الرسوم المتحركة وتجربة المستخدم العامة. تنشأ المشاكل الرئيسية من الاستدعاءات المفرطة لـ draw(_:)، والتخطيط غير الأمثل للعروض الفرعية، وعدم وجود تخزين مؤقت.

تجنب إعادة الرسم غير الضرورية

كل استدعاء لـ setNeedsDisplay() يؤدي إلى إعادة رسم كاملة للعرض. استخدم setNeedsDisplay(_:) مع مستطيل محدد إذا كانت التغييرات أثرت فقط على جزء من العرض. لخصائص CALayer (backgroundColor، cornerRadius، shadow)، لا يلزم إعادة الرسم — يتم تحديثها على مستوى GPU.

التخزين المؤقت للمحتوى المُعرض

إذا كان محتوى Custom UIView يتغير نادرًا، فاعرضه مرة واحدة في UIGraphicsImageRenderer واحفظه كـ UIImage. في إعادة الرسم التالية، استخدم draw(at:) لعرض الصورة المخزنة مؤقتًا — وهذا أسرع بعشرات المرات من إعادة العرض عبر Core Graphics.

swift
func renderToImage() -> UIImage {
    let renderer = UIGraphicsImageRenderer(size: bounds.size)
    return renderer.image { ctx in
        drawHierarchy(in: bounds, afterScreenUpdates: true)
    }
}

استخدم shouldRasterize للطبقات الثابتة

تعمل الخاصية shouldRasterize في CALayer على تمكين التخزين المؤقت للتمثيل النقطي للطبقة. فعّلها للعروض الثابتة ذات الشفافية والظلال — فهذا يقلل من حمل التركيب. عطّلها للعروض المتحركة: يتم إعادة تعيين المخزن المؤقت في كل تغيير، والتنقيط يؤدي فقط إلى تدهور الأداء.

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

هل من الضروري إعادة تعريف draw(_:) في Custom UIView؟

لا، draw(_:) مطلوب فقط للرسم المخصص عبر Core Graphics. إذا كان العرض مُجمّعًا من عروض فرعية قياسية (UILabel، UIImageView) ويستخدم CALayer، فلا حاجة لإعادة تعريف draw(_:) — بل سيحسن الأداء.

كيفية إضافة Custom UIView إلى Storyboard؟

ضع UIView عادي على اللوحة، في Identity Inspector حدد فصلك في حقل Class. إذا كانت الفئة موسومة بـ @IBDesignable، فستظهر التغييرات في الوقت الفعلي مباشرة في Storyboard.

ما الفرق بين init(frame:) و init(coder:)؟

يتم استدعاء init(frame:) عند إنشاء العرض برمجيًا — تمرر CGRect مع الموقع والحجم. يتم استدعاء init(coder:) عند إزالة التسلسل من Storyboard أو XIB. للتشغيل الصحيح، يجب تنفيذ كليهما، وإلا سيتعطل العرض عند التحميل من Interface Builder.

لماذا لا يتم استدعاء draw(_:)؟

السبب الأكثر شيوعًا هو أن العرض له إطار صفري (العرض أو الارتفاع يساوي صفرًا). لا يستدعي النظام draw(_:) للعروض ذات الأبعاد الصفرية. تحقق من الإطار في layoutSubviews() وتأكد من إضافة العرض إلى التسلسل الهرمي مع قيود صحيحة.

كيفية تحديث محتوى Custom UIView دون إعادة رسم كاملة؟

استخدم CALayer للخصائص التي تدعم الرسوم المتحركة على GPU (position، opacity، transform). للتحديثات الجزئية لـ draw(_:)، استخدم setNeedsDisplay(_:) مع CGRect للمنطقة المتغيرة — سيعيد النظام رسم المنطقة المحددة فقط، وليس العرض بأكمله.

الخلاصة

  • Custom UIView — فئة فرعية من UIView بطرق مُعاد تعريفها للرسم المخصص، تُستخدم عندما تكون مكونات UIKit القياسية غير كافية
  • draw(_:) — طريقة للرسم المخصص عبر Core Graphics، تُستدعى تلقائيًا بواسطة النظام؛ الاستدعاء المباشر ممنوع
  • CALayer — الطريقة المفضلة للرسومات المُسرّعة بـ GPU (الظلال، التدوير، الأشكال)، بديل لـ draw(_:) بأداء أعلى
  • init(frame:) و init(coder:) — المُهيئان الإلزاميان؛ يجب تنفيذ كليهما للتشغيل الصحيح من الكود وStoryboard
  • setNeedsDisplay() — إشارة للنظام لإعادة استدعاء draw(_:)؛ استخدم التحميل الزائد مع CGRect لإعادة الرسم الجزئي
  • @IBDesignable — سمة للمعاينة المباشرة لـ Custom UIView في Interface Builder، تبسط التطوير المرئي
  • للرسومات الثابتة، خزّن النتيجة مؤقتًا عبر UIGraphicsImageRenderer وارسم الصورة الجاهزة — فهذا يقلل من حمل CPU

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

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

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

اقرأ أيضًا