CGContext ใน iOS — คืออะไร วิธีการ และวิธีจัดการบริบทการวาด

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-07-21 เวลาอ่าน: 9 นาที

CGContext คืออ็อบเจกต์หลักของ Core Graphics สำหรับดำเนินการคำสั่งวาด 2D ใน iOS และ macOS โดยแสดงถึงบริบทกราฟิกที่เก็บสถานะปัจจุบัน: สีเติม ความหนาของเส้น การแปลง พื้นที่ตัด และแบบอักษร การดำเนินการวาดทั้งหมด — เส้น สี่เหลี่ยม ข้อความ รูปภาพ — ดำเนินการผ่าน CGContext ตาม Apple Developer Documentation, 2026 CGContext คือรากฐานของกราฟิกแรสเตอร์และเวกเตอร์ทั้งหมดในระบบนิเวศของ Apple

ประเด็นสำคัญ

  • CGContext คือบริบทกราฟิกที่ใช้ดำเนินการคำสั่ง Core Graphics 2D ทั้งหมด
  • 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 และสเกลสีเทา

การเติมพื้นที่ด้วยเกรเดียนต์จำเป็นต้องตั้งค่าพื้นที่ตัดก่อน หากไม่มี clip เกรเดียนต์จะเติมทั้งบริบท ไม่ใช่เฉพาะรูปร่างที่ต้องการ รูปแบบทั่วไป: เพิ่มเส้นทางไปยังบริบท เรียก clip เพื่อจำกัดพื้นที่ จากนั้นวาดเกรเดียนต์ รูปแบบนี้ใช้ในแอปพลิเคชันทั้งหมดที่ต้องการการเติมเกรเดียนต์ของรูปร่างที่ซับซ้อน ตั้งแต่ปุ่มไปจนถึงพื้นหลัง

ฟังก์ชันวัตถุประสงค์พื้นที่สี
setFillColorกำหนดสีเติมใดๆ (ผ่าน CGColor)
setStrokeColorกำหนดสีเส้นขอบใดๆ (ผ่าน CGColor)
drawLinearGradientวาดเกรเดียนต์เชิงเส้นRGB หรือสเกลสีเทา
drawRadialGradientวาดเกรเดียนต์รัศมีRGB หรือสเกลสีเทา

การวาดเส้นทางและเส้น

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 สำหรับงาน UI ส่วนใหญ่ที่มี 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 สำหรับการดำเนินการวาด 2D ทั้งหมดบน iOS และ macOS
  • saveGState และ restoreGState จัดการสแต็กสถานะบริบท ป้องกันผลข้างเคียง
  • setFillColor และ setStrokeColor กำหนดสีเติมและเส้นขอบผ่าน CGColorRef
  • addPath, strokePath, fillPath ทำงานกับเส้นโค้ง CGPath ตามต้องการ
  • clip จำกัดพื้นที่วาดให้อยู่ในเส้นทางปัจจุบันสำหรับมาสก์และการทำให้โค้งมน
  • translateCTM, scaleCTM, rotateCTM แปลงระบบพิกัดบริบท
  • การดำเนินการ CGContext ทั้งหมดทำงานบน CPU — สำหรับกราฟิกที่ซับซ้อน ใช้ CGLayer หรือ Metal

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม