CGContext في iOS — ما هو، الطرق وكيفية إدارة سياق الرسم

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

CGContext هو الكائن الرئيسي في Core Graphics لتنفيذ أوامر الرسم ثنائي الأبعاد في iOS و macOS. إنه يمثل سياقاً رسومياً يخزن الحالة الحالية: لون التعبئة، سمك الخط، التحويل، منطقة القص والخطوط. جميع عمليات الرسم — الخطوط، المستطيلات، النصوص، الصور — تُنفذ عبر CGContext. وفقاً لـ Apple Developer Documentation, 2026، يعتبر CGContext أساس جميع الرسومات النقطية والمتجهة في نظام Apple البيئي.

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

  • CGContext هو سياق الرسم الذي يتم من خلاله تنفيذ جميع أوامر Core Graphics ثنائية الأبعاد.
  • setStrokeColor و setFillColor يديران لون الخطوط والتعبئات في السياق.
  • addPath و drawPath تسمحان برسم مسارات CGPath عشوائية.
  • clip(to:) يحدد منطقة القص لتقييد الرسم بمنطقة محددة.
  • saveGState و restoreGState يحفظان ويستعيدان مكدس حالات السياق.

ما هو CGContext؟

CGContext هو نوع غير شفاف (opaque type) من إطار Core Graphics، يمثل بيئة لتنفيذ أوامر العرض ثنائي الأبعاد. يدير مكدس الحالات، مساحة الألوان الحالية، تحويل الإحداثيات ومنطقة القص. CGContext هو الكائن الوحيد الذي تتفاعل معه جميع وظائف رسم Quartz 2D — أي عملية رسومية تمر عبره.

يوجد CGContext بعدة أنواع حسب الغرض: bitmap context للعمل مع الصور النقطية في الذاكرة، PDF context لإنشاء مستندات PDF، window context للرسم على الشاشة (يوفره UIView في draw(_:)). يعمل مطورو iOS غالباً مع سياق يتم الحصول عليه من UIGraphicsGetCurrentContext() داخل طريقة draw(_:) أو من UIGraphicsBeginImageContextWithOptions للعرض دون اتصال.

يطبق السياق جميع الإعدادات عبر مكدس الحالات — يمكن للمطور حفظ الحالة الحالية (saveGState)، تعديل المعلمات واستعادة الحالة السابقة (restoreGState). هذا مهم بشكل حاسم عند رسم مشاهد معقدة حيث تتطلب العناصر المختلفة ألواناً مختلفة، وسماكات خطوط وتحويلات. وفقاً لـ Apple WWDC 2023، الاستخدام الصحيح لمكدس الحالات يحسن قابلية قراءة الكود ويمنع التشوهات البصرية.

إدارة الحالة: saveGState و restoreGState

CGContextSaveGState يضع نسخة من الحالة الحالية للسياق في المكدس. تشمل هذه الحالة جميع الإعدادات: الألوان، سمك الخط، التحويل، الظلال، منطقة القص، الخطوط. بعد تعديل المعلمات، استدعاء CGContextRestoreGState يعيد السياق إلى الحالة المحفوظة، ملغياً جميع التغييرات الوسيطة. يمكن للمكدس أن يحتوي على ما يصل إلى 32 حالة على iOS.

الاستخدام العملي للمكدس

عند رسم مجموعات من العناصر بأنماط مختلفة (مثل قطاعات رسم بياني متعددة الألوان)، يلغي مكدس الحالات الحاجة إلى تذكر واستعادة كل معلمة يدوياً. بدون saveGState/restoreGState، سيتعين على المطور إعادة تعيين اللون والظل والتحويل بعد كل عنصر. استخدام المكدس يقلل الكود ويمنع أخطاء الاستعادة.

من المهم الحفاظ على توازن الحفظ والاستعادة — كل saveGState يجب أن يكون له restoreGState مطابق. كسر التوازن يؤدي إلى تسرب الحالات أو استعادة مبكرة، مما يسبب عرضاً غير متوقع. لا يتحقق المحلل الثابت لـ Xcode من توازن مكدس CGContext، لذا تقع مسؤولية الصحة على عاتق المطور. يُوصى بتجميع الأزواج في كتل do/finally.

swift
override func draw(_ rect: CGRect) {
    guard let ctx = UIGraphicsGetCurrentContext() else { return }

    ctx.saveGState()
    ctx.setStrokeColor(UIColor.red.cgColor)
    ctx.setLineWidth(3)
    ctx.addRect(CGRect(x: 50, y: 50, width: 200, height: 100))
    ctx.strokePath()
    ctx.restoreGState()
}

الألوان والتعبئات

CGContextSetFillColorWithColor يضبط لون التعبئة للعمليات اللاحقة. يتم تمرير اللون كـ CGColorRef، الذي يحتوي على معلومات حول مساحة الألوان ومكونات اللون. CGContextSetStrokeColorWithColor يعمل بالمثل للون الحد. توصي Apple بتخزين قيم CGColorRef المستخدمة بشكل متكرر في ذاكرة تخزين مؤقت لتحسين الأداء.

التدرجات في CGContext

CGContextDrawLinearGradient يرسم تدرجاً خطياً بين نقطتين بألوان محددة. CGContextDrawRadialGradient ينشئ تدرجاً شعاعياً ينتقل من دائرة إلى أخرى. للعمل مع التدرجات، يتم أولاً إنشاء كائن CGGradientRef يحتوي على مصفوفة من الألوان ومواقع التوقف. تدعم التدرجات في CGContext كلاً من مساحات ألوان RGB و Grayscale.

تعبئة منطقة بتدرج يتطلب تعيين منطقة قص أولاً. بدون clip، سيملأ التدرج السياق بأكمله، وليس فقط الشكل المطلوب. النمط النموذجي: إضافة مسار إلى السياق، استدعاء clip لتقييد المنطقة، ثم رسم التدرج. يُستخدم هذا النمط في جميع التطبيقات التي تتطلب تعبئة تدرجية للأشكال المعقدة، من الأزرار إلى الخلفيات.

الدالةالغرضمساحة الألوان
setFillColorتضبط لون التعبئةأي (عبر CGColor)
setStrokeColorتضبط لون الحدأي (عبر CGColor)
drawLinearGradientترسم تدرجاً خطياًRGB أو Grayscale
drawRadialGradientترسم تدرجاً شعاعياًRGB أو Grayscale

رسم المسارات والخطوط

CGContextAddPath يضيف CGPathRef تم إنشاؤه مسبقاً إلى السياق للعرض اللاحق. بعد إضافة مسار، يمكن تحديده (strokePath)، تعبئته (fillPath) أو كليهما. CGContextBeginPath يبدأ مساراً جديداً، ماسحاً المسار السابق. يدعم السياق مساراً نشطاً واحداً فقط في كل مرة.

الخطوط المستقيمة والمنحنيات حسب النقاط

CGContextMoveToPoint ينقل نقطة المسار الحالية إلى الإحداثيات المحددة. CGContextAddLineToPoint يرسم خطاً مستقيماً من النقطة الحالية إلى النقطة المعطاة. CGContextAddCurveToPoint يضيف منحنى بيزيه مكعباً، CGContextAddQuadCurveToPoint يضيف منحنى تربيعياً. CGContextClosePath يغلق المسار بخط مستقيم بين النقطة الأخيرة والأولى.

على عكس UIBezierPath، حيث يتم استدعاء جميع هذه الطرق على كائن المسار، في CGContext يتم استدعاؤها مباشرة على السياق. هذا الاختلاف مهم عند اختيار النهج: UIBezierPath أكثر ملاءمة لإعادة استخدام المسارات، CGContext أفضل للرسم لمرة واحدة بأقل تكلفة إضافية. يدعم CGContext أيضاً الخطوط المتقطعة عبر CGContextSetLineDash، والنهايات المرنة عبر CGContextSetLineCap والوصلات عبر CGContextSetLineJoin.

swift
let ctx = UIGraphicsGetCurrentContext()!
ctx.beginPath()
ctx.moveTo(x: 50, y: 100)
ctx.addLineTo(x: 200, y: 100)
ctx.addCurveTo(x1: 250, y1: 50, x2: 300, y2: 150, x3: 350, y3: 100)
ctx.setStrokeColor(UIColor.blue.cgColor)
ctx.setLineWidth(5)
ctx.strokePath()

منطقة القص والتحويلات

CGContextClip يقيّد منطقة الرسم بالمسار الحالي. جميع عمليات الرسم اللاحقة ستكون مرئية فقط داخل هذه المنطقة. هذه هي الآلية الأساسية للإخفاء، تدوير الزوايا وإنشاء حدود مخصصة للعناصر. يتم أيضاً حفظ منطقة القص واستعادتها عبر saveGState/restoreGState.

تحويلات الإحداثيات

CGContextTranslateCTM يزيح أصل نظام إحداثيات السياق. CGContextScaleCTM يقيس محوري X و Y. CGContextRotateCTM يدير نظام الإحداثيات حول الأصل الحالي. تتراكم التحويلات — كل تحويل لاحق يُطبق بالنسبة لنظام الإحداثيات الحالي. تُستخدم التحويلات المركبة (الإزاحة + التدوير + القياس) للرسوم المتحركة وتحديد موضع العناصر.

الجمع بين القص والتحويلات يوفر إمكانيات قوية: يمكن رسم قناع معقد عبر القص، ثم تطبيق تحويل على المحتوى بأكمله. مثال نموذجي — عرض جزء من الصورة بزاوية مع زوايا مدورة. ترتيب العمليات مهم: أولاً تعيين منطقة القص، ثم تطبيق التحويل، ثم رسم المحتوى. توصي Apple بتقليل عدد تغييرات CTM (مصفوفة التحويل الحالية) في حلقة الرسم.

swift
override func draw(_ rect: CGRect) {
    guard let ctx = UIGraphicsGetCurrentContext() else { return }
    ctx.saveGState()

    // clipping region - circle
    ctx.addEllipse(in: CGRect(x: 50, y: 50, width: 200, height: 200))
    ctx.clip()

    // content transformation
    ctx.translateBy(x: 150, y: 150)
    ctx.rotate(by: .pi / 4)
    ctx.translateBy(x: -150, y: -150)

    UIImage(named: "photo")?.draw(at: CGPoint(x: 50, y: 50))
    ctx.restoreGState()
}

مثال رسم في CGContext

لننظر إلى مثال كامل لإنشاء رسم بياني متدرج باستخدام CGContext. يتضمن الرسم البياني ثلاثة قطاعات بألوان مختلفة مع تعبئة متدرجة وتسميات نصية. يوضح الكود مزيجاً من saveGState و clip والتدرجات ورسم النص في سياق واحد.

swift
class PieChartView: UIView {

    private struct Slice {
        let color: UIColor
        let value: CGFloat
        let label: String
    }

    override func draw(_ rect: CGRect) {
        guard let ctx = UIGraphicsGetCurrentContext() else { return }

        let slices = [
            Slice(color: .systemRed, value: 0.4, label: "iOS"),
            Slice(color: .systemGreen, value: 0.35, label: "Android"),
            Slice(color: .systemBlue, value: 0.25, label: "Other")
        ]

        let center = CGPoint(x: bounds.midX, y: bounds.midY)
        let radius = bounds.width * 0.35
        var startAngle: CGFloat = -.pi / 2

        for slice in slices {
            let endAngle = startAngle + .pi * 2 * slice.value

            ctx.saveGState()
            ctx.moveTo(x: center.x, y: center.y)
            ctx.addArc(center: center, radius: radius,
                       startAngle: startAngle, endAngle: endAngle, clockwise: false)
            ctx.closePath()
            ctx.setFillColor(slice.color.cgColor)
            ctx.fillPath()

            // text label
            let midAngle = startAngle + endAngle / 2
            let labelPos = CGPoint(
                x: center.x + radius * 0.7 * cos(midAngle),
                y: center.y + radius * 0.7 * sin(midAngle)
            )
            slice.label.draw(at: labelPos, withAttributes: [
                .foregroundColor: UIColor.white,
                .font: UIFont.boldSystemFont(ofSize: 14)
            ])
            ctx.restoreGState()
            startAngle = endAngle
        }
    }
}

في هذا المثال، يتم رسم كل قطاع من الرسم البياني في كتلة saveGState/restoreGState الخاصة به. هذا النهج يضمن أن إعدادات اللون والمسار لقطاع واحد لا تؤثر على القطاعات الأخرى. تنشئ طريقة addArc قوساً من الزاوية البداية إلى النهاية، ويغلق closePath القطاع نحو المركز. يتم وضع نص التسمية على الزاوية الوسطى للقطاع باستخدام الدوال المثلثية cos و sin لحساب الموضع.

لتحسين الأداء عند إعادة الرسم المتكررة، يُوصى بتخزين حسابات الزوايا والمواضع في مصفوفة منفصلة. CGContext ينفذ جميع العمليات على وحدة المعالجة المركزية CPU، لذا قد تتطلب المشاهد المعقدة ذات العشرات من القطاعات تحسيناً — مثل العرض المسبق في CGLayer أو استخدام Metal لتسريع GPU. بالنسبة لمعظم مهام واجهة المستخدم التي تحتوي على 5–10 عناصر، يبقى أداء CGContext مقبولاً.

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

كيفية الحصول على CGContext الحالي في iOS؟

استدعِ UIGraphicsGetCurrentContext() داخل طريقة draw(_:) لـ UIView أو داخل كتلة UIGraphicsBeginImageContextWithOptions. لطبقات CALayer، استخدم طريقة draw(in:) الخاصة بـ CALayerDelegate، التي تتلقى سياقاً جاهزاً.

ما الفرق بين fillPath و strokePath؟

fillPath يملأ المنطقة الداخلية للمسار الحالي بلون setFillColor. strokePath يرسم خطاً على طول المسار بمعلمات setStrokeColor و setLineWidth و setLineCap. يتم تنفيذ كلتا العمليتين بالتسلسل: أولاً التعبئة، ثم الحد عند الضرورة.

ما هو bitmap CGContext؟

Bitmap CGContext هو سياق تم إنشاؤه عبر CGBitmapContextCreate يرسم في مخزن ذاكرة بدلاً من الشاشة. يُستخدم لتوليد الصور في الخلفية، معالجة الرسومات بدون عرض وإنشاء مواد لـ Metal أو OpenGL.

كيفية إعادة تعيين جميع إعدادات CGContext؟

لا توفر Core Graphics طريقة لإعادة تعيين جميع الإعدادات. استخدم saveGState قبل التغييرات و restoreGState للعودة إلى الحالة الأصلية. بدلاً من ذلك، يمكن إنشاء سياق جديد بنفس المعلمات.

لماذا لا يظهر التدرج داخل الشكل؟

قبل استدعاء drawLinearGradient، قم بتعيين منطقة القص عبر clip على المسار المطلوب. بدون clip، يملأ التدرج السياق بأكمله. الترتيب: addPath ← clip ← drawLinearGradient — يضمن التدرج داخل الشكل.

الخلاصة

  • CGContext هو الكائن المركزي في Core Graphics لجميع عمليات الرسم ثنائي الأبعاد على iOS و macOS.
  • saveGState و restoreGState يديران مكدس حالات السياق، مما يمنع الآثار الجانبية.
  • setFillColor و setStrokeColor يضبطان ألوان التعبئة والحد عبر CGColorRef.
  • addPath و strokePath و fillPath تعمل مع مسارات CGPath عشوائية.
  • clip يقيّد منطقة الرسم بالمسار الحالي للأقنعة والتدوير.
  • translateCTM و scaleCTM و rotateCTM تحول نظام إحداثيات السياق.
  • جميع عمليات CGContext تُنفذ على CPU — للرسومات المعقدة استخدم CGLayer أو Metal.

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

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

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

اقرأ أيضًا