CGContext — شیئی اساسی Core Graphics برای اجرای دستورات نقاشی 2D در iOS و macOS است. آن یک زمینه گرافیکی را نمایش میدهد که وضعیت فعلی را ذخیره میکند: رنگ پرکننده، ضخامت خط، تبدیل، ناحیه برش و فونتها. همه عملیات نقاشی — خطوط، مستطیلها، متن، تصاویر — از طریق CGContext انجام میشوند. به گزارش Apple Developer Documentation, 2026، CGContext پایه و اساس کل گرافیک راستری و وکتوری در اکوسیستم Apple است.
نکات کلیدی
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، استفاده صحیح از پیشکار وضعیتها خوانایی کد را افزایش و از ایجاد آرتفاکتهای دیداری جلوگیری میکند.
CGContextSaveGState یک کپی از وضعیت فعلی زمینه را در پیشکار قرار میدهد. این وضعیت تمام تنظیمات را شامل میشود: رنگها، ضخامت خط، تبدیل، سایهها، ناحیه برش، فونتها. پس از تغییر پارامترها، فرخوانی CGContextRestoreGState زمینه را به وضعیت ذخیرهشده بازمیگرداند و تمام تغییرات وسطی را لغو میکند. پیشکار میتواند تا 32 وضعیت را در iOS نگه دارد.
هنگام نقاشی گروهی از عناصر با سبکهای مختلف (به عنوان مثال، بخشهای رنگارنگ دیاگرام) پیشکار وضعیتها نیاز به به خاطر سپردن و بازیابی دستی هر پارامتر را از بین میبرد. بدون saveGState/restoreGState توسعهدهنده مجبور بود پس از هر عنصر رنگ، سایه و تبدیل را مجدداً تنظیم کند. استفاده از پیشکار کد را کوتاهتر کرده و خطاهای بازیابی را از بین میبرد.
حفظ تعادل ذخیره و بازیابی مهم است — هر saveGState باید یک restoreGState متناظر داشته باشد. نقض تعادل منجر به نشت وضعیتها یا بازیابی زودهنگام میشود که نمایش غیرقابل پیشبینی را به همراه دارد. آنالیزور استاتیک Xcode تعادل پیشکار CGContext را بررسی نمیکند، بنابراین مسئولیت صحت بر عهده توسعهدهنده است. گروهبندی جفتها در بلوکهای do/finally توصیه میشود.
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 های پراستفاده را برای افزایش کارایی توصیه میکند.
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 پشتیبانی میکند.
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) را در چرخه رندر به حداقل توصیه میکند.
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 را بررسی میکنیم. نمودار شامل سه بخش با رنگهای مختلف با پرکننده گرادینتی و برچسبهای متنی است. کد ترکیبی از saveGState، clip، گرادینتها و رندر متن را در یک زمینه نمایش میدهد.
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 قابل قبول است.
سؤالات متداول
داخل روش draw(_:) UIView یا داخل بلوک UIGraphicsBeginImageContextWithOptions UIGraphicsGetCurrentContext() را فراخوان کنید. برای لایههای CALayer از draw(in:) دلگیت CALayerDelegate استفاده کنید که یک زمینه آماده دریافت میکند.
fillPath ناحیه داخلی مسیر فعلی را با رنگ setFillColor پر میکند. strokePath با پارامترهای setStrokeColor، setLineWidth و setLineCap در امتداد مسیر خط نقش میکند. هر دو عملیات به صورت پیشاپیش انجام میشوند: ابتدا fill، سپس در صورت نیاز stroke.
Bitmap CGContext — زمینهای است که از طریق CGBitmapContextCreate ایجاد شده و به جای صفحه نمایش در بافر حافظه نقش میکند. برای تولید تصویر پسزمینه، پردازش گرافیک بدون نمایش و ایجاد بافت برای Metal یا OpenGL استفاده میشود.
Core Graphics روشی برای بازنشانی کل تنظیمات ارائه نمیدهد. از saveGState قبل از تغییرات و restoreGState برای بازگشت به وضعیت اولیه استفاده کنید. به طور جایگزین میتوان یک زمینه جدید با همان پارامترها ایجاد کرد.
قبل از فرخوانی drawLinearGradient، ناحیه برش را با clip روی مسیر مورد نظر تنظیم کنید. بدون clip، گرادینت کل زمینه را پر میکند. ترتیب: addPath → clip → drawLinearGradient — گرادینت داخل شکل را تضمین میکند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید