draw(_:) / drawRect هي طريقة من فئة UIView (في Swift) ونظيرتها في Objective-C (drawRect:)، المسؤولة عن عرض محتوى العرض باستخدام Core Graphics. يستدعي النظام هذه الطريقة تلقائياً عند أول ظهور للعرض على الشاشة وبعد استدعاء setNeedsDisplay(). وفقاً لوثائق Apple (2025)، فإن draw(_:) هي الطريقة الوحيدة التي يمكن للمطور فيها الوصول إلى سياق الرسوم للشاشة الحالية للرسم المخصص. يتيح تجاوز draw(_:) السيطرة الكاملة على مظهر المكون — من الأشكال الهندسية البسيطة إلى الرسوم المتحركة المعقدة.
النقاط الرئيسية
draw(_:) هي طريقة实例 من UIView يستدعيها UIKit لعرض محتوى العرض. داخل هذه الطريقة، يحصل المطور على وصول إلى سياق الرسوم CGContext ويستخدم واجهة برمجة Core Graphics لرسم الخطوط والتعبئات والنصوص والصور. تقوم drawRect: في Objective-C بنفس الوظيفة ولكن بصيغة مختلفة: المعامل الوحيد هو CGRect يحدد المنطقة المراد إعادة رسمها.
وفقاً لـ Apple Engineering (2024)، تعمل draw(_:) من خلال عرض Bitmap Graphics Context المعتمد على CPU، مما يوفر أقصى مرونة ولكنه يتطلب موارد أكثر مقارنة بـ CALayer. يتم اتخاذ قرار استخدام draw(_:) بناءً على تعقيد الرسوم ومتطلبات الأداء.
في Swift، يتم تعريف الطريقة كـ override func draw(_ rect: CGRect)، حيث rect هو المستطيل الذي يحتاج إلى إعادة رسم. في Objective-C، التوقيع هو - (void)drawRect:(CGRect)rect. قد يكون معامل rect أصغر من bounds العرض أثناء إعادة الرسم الجزئي عبر setNeedsDisplay(_:).
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
يستدعي النظام draw(_:) تلقائياً في سيناريوهات محددة بدقة. يساعد فهم هذه المحفزات في تجنب عمليات إعادة الرسم غير الضرورية وتحسين أداء العرض. فيما يلي جميع حالات الاستدعاء التلقائي للطريقة.
تحذر وثائق Apple: لا تستدعِ draw(_:) مباشرة أبداً. النظام نفسه يقرر متى ينفذ الرسم، والاستدعاء المباشر يعطل آلية التخزين المؤقت الداخلية. لطلب إعادة الرسم، استخدم دائماً setNeedsDisplay() أو setNeedsDisplay(_:).
يبدأ تجاوز draw(_:) في Swift بالحصول على سياق الرسوم ثم استدعاءات Core Graphics اللاحقة. يُنصح بإنشاء طرق منفصلة للكتل المنطقية للرسم — مما يحسن قابلية قراءة الكود واختباره.
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
// Line parameters
context.setStrokeColor(UIColor.darkGray.cgColor)
context.setLineWidth(2.0)
// Drawing a triangle
context.move(to: CGPoint(x: rect.midX, y: rect.minY + 10))
context.addLine(to: CGPoint(x: rect.maxX - 10, y: rect.maxY - 10))
context.addLine(to: CGPoint(x: rect.minX + 10, y: rect.maxY - 10))
context.closePath()
context.strokePath()
}
في هذا المثال، يرسم draw(_:) مثلثاً بمخطط رمادي داكن بسمك 2 بكسل. يوصي Apple باستدعاء super.draw(rect) في بداية الطريقة للحفاظ على منطق الرسم الأصلي، على الرغم من أن تنفيذ draw(_:) الافتراضي في UIView فارغ.
يجب أن يعطي كل استدعاء لـ draw(_:) نتيجة متطابقة مع نفس بيانات الإدخال. يتيح ذلك للنظام تخزين النتيجة مؤقتاً وعدم استدعاء draw(_:) مرة أخرى إذا لم يتغير محتوى العرض. لا تستخدم قيماً عشوائية أو وقت النظام أو طلبات الشبكة داخل draw(_:).
drawRect: هي تاريخياً أول إصدار للطريقة، ظهرت في iOS 2.0 مع Objective-C. في Swift، تمت إعادة تسمية الطريقة إلى draw(_:) باستخدام المعامل الخارجي _. وظيفياً، الطريقتان متطابقتان: كلتاهما تستقبل CGRect للمنطقة المراد إعادة رسمها وتستخدم UIGraphicsGetCurrentContext() للوصول إلى سياق الرسوم.
| الخاصية | draw(_:) (Swift) | drawRect: (Objective-C) |
|---|---|---|
| التوقيع | override func draw(_ rect: CGRect) | - (void)drawRect:(CGRect)rect |
| استدعاء super | super.draw(rect) | [super drawRect:rect] |
| السياق | UIGraphicsGetCurrentContext() | UIGraphicsGetCurrentContext() |
| معامل rect | rect: CGRect | CGRect rect |
| الأداء | متطابق | متطابق |
عند ترحيل مشروع من Objective-C إلى Swift، تعد إعادة تسمية الطريقة إحدى المهام الأولى. يوفر Xcode محولاً تلقائياً، لكن drawRect: يتطلب تحديثاً يدوياً إلى draw(_:). وفقاً لـ Apple (2024)، فإن إصدار Swift draw(_:) مفضل للمشاريع الجديدة.
يتم تنفيذ draw(_:) على CPU، ويمكن أن يتسبب التنفيذ غير الأمثل في انخفاض معدل الإطارات وضعف الأداء. توصي Apple بعدة طرق مجربة لتسريع الرسم.
كل عملية Core Graphics (move(to:), addLine(to:), strokePath) لها تكلفة إضافية. قم بتجميع العمليات واستخدم CGPath للأشكال المعقدة — يتم إنشاء المسار مرة واحدة وإعادة استخدامه في كل استدعاء لـ draw(_:).
UIBezierPath هو غلاف Objective-C حول CGPath يوفر واجهة برمجة بسيطة لإنشاء الأشكال. أنشئ UIBezierPath مسبقاً (مثلاً في المُهيئ) واستدعِ ببساطة fill() أو stroke() داخل draw(_:).
private let starPath: UIBezierPath = {
let path = UIBezierPath()
// Building a star shape
path.move(to: CGPoint(x: 50, y: 0))
for i in 1...5 {
let angle = CGFloat(i) * 4 * CGFloat.pi / 5
path.addLine(to: CGPoint(x: 50 + 40 * cos(angle),
y: 50 + 40 * sin(angle)))
}
path.close()
return path
}()
override func draw(_ rect: CGRect) {
UIColor.systemYellow.setFill()
starPath.fill()
}
غالباً ما يرتكب المطورون نفس الأخطاء عند تجاوز draw(_:). تساعد معرفة هذه الأنماط في تجنب الأخطاء وتدهور الأداء. دعنا نلقي نظرة على المشكلات الأكثر شيوعاً وحلولها.
باتباع هذه القواعد، ستضمن رسماً مخصصاً ثابتاً وسريعاً في أي مشروع iOS. قم بتحليل draw(_:) عبر Instruments (Core Animation) لرؤية وقت التنفيذ الفعلي والاختناقات.
الأسئلة الشائعة
لا، الاستدعاء المباشر لـ draw(_:) محظور من قبل وثائق Apple. يدير النظام دورة الرسم بنفسه. لطلب إعادة الرسم، استخدم setNeedsDisplay()، الذي يحدد العرض بشكل صحيح على أنه يحتاج إلى تحديث في أقرب دورة عرض.
وظيفياً هاتان الطريقتان متطابقتان. drawRect: يُستخدم في Objective-C، draw(_:) في Swift. كلتاهما تستقبل CGRect لمنطقة إعادة الرسم وتستخدمان نفس سياق Core Graphics عبر UIGraphicsGetCurrentContext().
يحسن Apple العرض: إذا لم يكن لدى UIView draw(_:) مُعاد تعريفه، لا ينشئ النظام سياق bitmap له. هذا يوفر الذاكرة. إذا كان التجاوز موجوداً ولكن الطريقة لا تُستدعى — تحقق من أن إطار العرض غير صفري وأن العرض مرئي.
فقط عند الحاجة: العرض الأول، بعد setNeedsDisplay()، عند تغيير bounds مع contentMode = .redraw. في الحالة الثابتة، لا يتم استدعاء draw(_:) مرة أخرى، مما يوفر موارد CPU والبطارية.
يوصي Apple باستدعاء super.draw(rect) في بداية الطريقة المُعاد تعريفها. على الرغم من أن التنفيذ الحالي لـ UIView فارغ، فإن استدعاء super يضمن التوافق مع الإصدارات المستقبلية من UIKit وهو ممارسة جيدة.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.