CGContext در iOS — چیست، روش‌ها و چگونه زمینه ترسیم را مدیریت کنیم

نویسنده: IT Sectr منتشر شده: 2026-07-21 زمان مطالعه: 9 دقیقه

CGContext — شیئی اساسی Core Graphics برای اجرای دستورات نقاشی 2D در iOS و macOS است. آن یک زمینه گرافیکی را نمایش می‌دهد که وضعیت فعلی را ذخیره می‌کند: رنگ پرکننده، ضخامت خط، تبدیل، ناحیه برش و فونت‌ها. همه عملیات نقاشی — خطوط، مستطیل‌ها، متن، تصاویر — از طریق CGContext انجام می‌شوند. به گزارش Apple Developer Documentation, 2026، CGContext پایه و اساس کل گرافیک راستری و وکتوری در اکوسیستم Apple است.

نکات کلیدی

  • CGContext — زمینه گرافیکی که تمام دستورات 2D Core Graphics از طریق آن اجرا می‌شوند.
  • setStrokeColor و setFillColor رنگ خطوط و پرکننده‌ها را در زمینه مدیریت می‌کنند.
  • addPath و drawPath امکان ترسیم کانتورهای دلخواه CGPath را فراهم می‌کنند.
  • clip(to:) ناحیه برش را برای محدود کردن نقاشی به یک ناحیه مشخص تعیین می‌کند.
  • saveGState و restoreGState پیشکار وضعیت‌های زمینه را ذخیره و بازیابی می‌کنند.

CGContext چیست؟

CGContext — یک نوع غیرشفاف (opaque type) از چارچوب Core Graphics است که محیطی برای اجرای دستورات رندر 2D فراهم می‌کند. آن پیشکار وضعیت‌ها، فضای رنگ فعلی، تبدیل مختصات و ناحیه برش را مدیریت می‌کند. 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 (Current Transformation Matrix) را در چرخه رندر به حداقل توصیه می‌کند.

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

    // ناحیه برش — دایره
    ctx.addEllipse(in: CGRect(x: 50, y: 50, width: 200, height: 200))
    ctx.clip()

    // تبدیل محتوا
    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()

            // برچسب متنی
            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. برای اکثر وظایف UI با 5–10 عنصر، کارایی CGContext قابل قبول است.

سؤالات متداول

چگونه CGContext فعلی را در iOS به دست آوریم؟

داخل روش draw(_:) UIView یا داخل بلوک UIGraphicsBeginImageContextWithOptions UIGraphicsGetCurrentContext() را فراخوان کنید. برای لایه‌های CALayer از draw(in:) دلگیت CALayerDelegate استفاده کنید که یک زمینه آماده دریافت می‌کند.

تفاوت بین fillPath و strokePath چیست؟

fillPath ناحیه داخلی مسیر فعلی را با رنگ setFillColor پر می‌کند. strokePath با پارامترهای setStrokeColor، setLineWidth و setLineCap در امتداد مسیر خط نقش می‌کند. هر دو عملیات به صورت پیشاپیش انجام می‌شوند: ابتدا fill، سپس در صورت نیاز stroke.

Bitmap CGContext چیست؟

Bitmap CGContext — زمینه‌ای است که از طریق CGBitmapContextCreate ایجاد شده و به جای صفحه نمایش در بافر حافظه نقش می‌کند. برای تولید تصویر پس‌زمینه، پردازش گرافیک بدون نمایش و ایجاد بافت برای Metal یا OpenGL استفاده می‌شود.

چگونه تمام تنظیمات CGContext را بازنشانی کنیم؟

Core Graphics روشی برای بازنشانی کل تنظیمات ارائه نمی‌دهد. از saveGState قبل از تغییرات و restoreGState برای بازگشت به وضعیت اولیه استفاده کنید. به طور جایگزین می‌توان یک زمینه جدید با همان پارامترها ایجاد کرد.

چرا گرادینت داخل شکل نمایش داده نمی‌شود؟

قبل از فرخوانی drawLinearGradient، ناحیه برش را با clip روی مسیر مورد نظر تنظیم کنید. بدون clip، گرادینت کل زمینه را پر می‌کند. ترتیب: addPath → clip → drawLinearGradient — گرادینت داخل شکل را تضمین می‌کند.

خلاصه

  • CGContext — شیئی مرکزی Core Graphics برای تمام عملیات نقاشی 2D در iOS و macOS.
  • saveGState و restoreGState پیشکار وضعیت‌های زمینه را مدیریت کرده و از اثرات جانبی جلوگیری می‌کنند.
  • setFillColor و setStrokeColor رنگ‌های پرکننده و کانتور را از طریق CGColorRef تنظیم می‌کنند.
  • addPath، strokePath، fillPath با کانتورهای دلخواه CGPath کار می‌کنند.
  • clip ناحیه نقاشی را به مسیر فعلی برای ماسک‌ها و گردی‌ها محدود می‌کند.
  • translateCTM، scaleCTM، rotateCTM سیستم مختصات زمینه را تبدیل می‌کنند.
  • همه عملیات CGContext در CPU انجام می‌شوند — برای گرافیک پیچیده از CGLayer یا Metal استفاده کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید