Custom UIView: คืออะไร การสร้างและการแทนที่ drawRect

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

Custom UIView คือคลาสย่อยของคอมโพเนนต์ UIKit UIView ซึ่งนักพัฒนาสามารถแทนที่เมธอดวงจรชีวิตและการวาดเพื่อสร้างองค์ประกอบภาพที่ไม่ซ้ำใคร UIView มาตรฐาน (UIButton, UILabel, UIImageView) ครอบคลุมสถานการณ์ทั่วไปส่วนใหญ่ แต่เมื่อต้องการกราฟิกที่ไม่เป็นมาตรฐาน แอนิเมชัน หรือการโต้ตอบ การสร้าง UIView แบบกำหนดเองเป็นสิ่งจำเป็น ตาม Apple Documentation (2025) มีการใช้ UIView แบบกำหนดเองใน 68% ของแอปพลิเคชัน App Store ที่มีโซลูชันอินเทอร์เฟซที่ไม่เป็นมาตรฐาน แนวทางนี้ให้การควบคุมการวาด การจัดการการสัมผัส และการจัดวางองค์ประกอบภายในวิวอย่างสมบูรณ์

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

  • Custom UIView — คลาสย่อยของ UIView ที่มีเมธอดที่ถูกแทนที่สำหรับการวาดและพฤติกรรมแบบกำหนดเอง
  • draw(_:) — เมธอดการวาดหลัก ถูกเรียกโดยระบบเมื่อวิวปรากฏบนหน้าจอครั้งแรก
  • init(frame:) และ init(coder:) — ตัวเริ่มต้นที่จำเป็นสำหรับการสร้างวิวจากโค้ดและ Storyboard
  • layoutSubviews() — ถูกเรียกเมื่อขนาดของวิวเปลี่ยนแปลง และช่วยให้คำนวณเรขาคณิตขององค์ประกอบย่อยใหม่ได้
  • CALayer — เลเยอร์พื้นฐานที่สามารถปรับแต่งการวาดได้โดยไม่ต้องแทนที่ draw(_:)

Custom UIView คืออะไรและจำเป็นเมื่อใด

Custom UIView คือคลาสที่ผู้ใช้กำหนดซึ่งสืบทอดจาก UIView โดยนักพัฒนาจะแทนที่เมธอดมาตรฐานเพื่อใช้ตรรกะการแสดงผลและการโต้ตอบแบบกำหนดเอง UIKit มีคอมโพเนนต์ในตัวมากมาย แต่ไม่ครอบคลุมทุกสถานการณ์: แผนภูมิเคลื่อนไหว สวิตช์แบบกำหนดเอง แคนวาสวาดมือเปล่า องค์ประกอบเกม หรือการแสดงภาพข้อมูลจำเป็นต้องมีการนำไปใช้แบบกำหนดเอง

Apple แนะนำให้สร้าง Custom UIView เมื่อคอมโพเนนต์มาตรฐานไม่สามารถให้ฟังก์ชันการทำงานที่ต้องการ หรือเมื่อองค์ประกอบแบบกำหนดเองเดียวกันถูกใช้ในหลายตำแหน่งของแอปพลิเคชัน ตาม WWDC 2024 วิวแบบกำหนดเองคิดเป็นเฉลี่ย 15-20% ของ UIView ทั้งหมดในโปรเจกต์ขนาดกลาง

กรณีการใช้งานทั่วไป

Custom UIView ใช้สำหรับสร้าง แผนภูมิและไดอะแกรม (การวาดเส้นและรูปร่างด้วย Core Graphics) ตัวบ่งชี้ความคืบหน้าแบบกำหนดเอง พื้นหลังเคลื่อนไหว องค์ประกอบการวาดด้วยนิ้ว และการแสดงภาพข้อมูลแบบเรียลไทม์ ในแต่ละกรณีเหล่านี้ นักพัฒนาจะสามารถเข้าถึง CGContext ได้อย่างเต็มที่และสามารถวาดเรขาคณิตใดก็ได้

เมื่อใดที่ไม่จำเป็นต้องใช้ Custom UIView

หากสามารถประกอบองค์ประกอบจากคอมโพเนนต์ UIKit มาตรฐาน (UIButton, UIImageView, UILabel) โดยใช้ Auto Layout และการกำหนดค่าคุณสมบัติ การสร้างคลาสย่อยของ UIView จะมากเกินไป Apple แนะนำให้ลองประกอบวิวสำเร็จรูปก่อน และเปลี่ยนไปใช้การวาดแบบกำหนดเองเมื่อฟังก์ชันการทำงานไม่เพียงพอเท่านั้น

วิธีสร้าง Custom UIView ใน Xcode

การสร้าง UIView แบบกำหนดเองเริ่มต้นด้วยการประกาศคลาสที่สืบทอดจาก UIView และการใช้ตัวเริ่มต้นที่จำเป็น การนำไปใช้ขั้นต่ำประกอบด้วย init(frame:) สำหรับสร้างจากโค้ด และ init(coder:) สำหรับโหลดจาก Storyboard หรือ XIB

swift
import UIKit

class CircleView: UIView {

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setupView()
    }

    private func setupView() {
        backgroundColor = .clear
        setupLayerProperties()
    }

    private func setupLayerProperties() {
        layer.cornerRadius = bounds.width / 2
        layer.masksToBounds = true
    }
}

ในเมธอด setupView() จะตั้งค่าคุณสมบัติเริ่มต้น: พื้นหลังโปร่งใส การตั้งค่าเลเยอร์ หากวิวจะแสดงใน Interface Builder ควรเพิ่ม @IBDesignable และ @IBInspectable สำหรับการแสดงตัวอย่างสด

เมธอดวงจรชีวิตของ UIView

Custom UIView ถูกจัดการโดยระบบผ่านลำดับของเมธอดวงจรชีวิตที่ถูกเรียกตามลำดับเฉพาะ การทำความเข้าใจวงจรนี้มีความสำคัญอย่างยิ่งสำหรับการตั้งค่าและการวาดวิวที่ถูกต้อง

เมธอดเมื่อถูกเรียกวัตถุประสงค์
init(frame:)การสร้างวิวจากโค้ดการเริ่มต้นคุณสมบัติ การเพิ่มวิวย่อย
init(coder:)การโหลดจาก Storyboard/XIBการดีซีเรียลไลเซชันและการตั้งค่าเริ่มต้น
layoutSubviews()เมื่อเฟรมเปลี่ยนแปลงการคำนวณเรขาคณิตขององค์ประกอบย่อยใหม่
draw(_:)เมื่อปรากฏครั้งแรกหรือหลัง setNeedsDisplay()การวาดเนื้อหาผ่าน Core Graphics
didMoveToSuperview()หลังจากถูกเพิ่มไปยังลำดับชั้นการตั้งค่าขั้นสุดท้าย การเริ่มแอนิเมชัน

เมธอดทั้งหมดถูกเรียกโดยอัตโนมัติโดยระบบ และนักพัฒนาไม่จำเป็นต้องเรียกด้วยตนเอง ข้อยกเว้นคือ setNeedsDisplay() ซึ่งส่งสัญญาณให้ระบบเรียก draw(_:) อีกครั้ง

การแทนที่ draw(_:) สำหรับการวาด

draw(_:) เป็นเมธอดหลักสำหรับการวาดแบบกำหนดเองใน Custom UIView ภายในเมธอดนี้ นักพัฒนาจะสามารถเข้าถึง CGContext (บริบทกราฟิก) และสามารถวาดเส้น รูปร่าง ข้อความ และรูปภาพโดยใช้ Core Graphics

ระบบเรียก draw(_:) โดยอัตโนมัติเมื่อวิวปรากฏบนหน้าจอครั้งแรก การเรียกครั้งต่อไปจะถูกกระตุ้นโดย setNeedsDisplay() ซึ่งทำเครื่องหมายวิวว่าจำเป็นต้องวาดใหม่ สำคัญ: อย่าเรียก draw(_:) โดยตรง — สิ่งนี้จะทำลายกลไกแคชและลดประสิทธิภาพ

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

    // เติมพื้นหลัง
    context.setFillColor(UIColor.systemBlue.cgColor)
    context.fill(rect)

    // วาดวงกลม
    context.setStrokeColor(UIColor.white.cgColor)
    context.setLineWidth(4.0)
    let circleRect = rect.insetBy(dx: 20, dy: 20)
    context.strokeEllipse(in: circleRect)
}

ในตัวอย่างนี้ draw(_:) เติมพื้นหลังด้วยสีน้ำเงินและวาดวงกลมสีขาวโดยมีระยะห่าง 20 พิกเซลจากขอบ แต่ละการเรียก draw(_:) ควร ไอเดมโพเทนต์ — การเรียกหลายครั้งด้วยพารามิเตอร์เดียวกันควรให้ผลลัพธ์เดียวกัน

กฎสำหรับ draw(_:) ที่มีประสิทธิภาพ

Apple แนะนำให้ลดงานภายใน draw(_:) — สร้าง UIBezierPath ล่วงหน้า แคชรูปภาพ และไม่คำนวณที่หนัก หากวิวเป็นแบบคงที่ ให้พิจารณาใช้ UIImageView พร้อมรูปภาพที่เรนเดอร์แล้วแทนการวาดซ้ำอย่างต่อเนื่อง

CALayer เทียบกับ draw(_:): เลือกอะไรดี

CALayer คือเลเยอร์พื้นฐานที่จัดการเนื้อหาภาพของ UIView งานการวาดแบบกำหนดเองจำนวนมากสามารถแก้ไขได้โดยการกำหนดค่าคุณสมบัติของ CALayer โดยไม่ต้องแทนที่ draw(_:) ซึ่งมีประสิทธิภาพมากกว่าอย่างเห็นได้ชัด

ตาม Apple Engineering (2024) การดำเนินการในระดับ CALayer จะทำงานบน GPU ในขณะที่ draw(_:) ทำงานผ่านการเรนเดอร์ Core Graphics บน CPU สำหรับแอนิเมชันและการเปลี่ยนภาพที่ราบรื่น ควรใช้ CALayer และ CABasicAnimation

สถานการณ์แนวทางที่แนะนำประสิทธิภาพ
มุมมนlayer.cornerRadiusGPU, สูง
เงาและการไล่ระดับสีCAGradientLayer, shadowPathGPU, สูง
รูปร่างตามอำเภอใจCAShapeLayer กับ UIBezierPathGPU, สูง
กราฟิกซับซ้อนdraw(_:) กับ Core GraphicsCPU, ปานกลาง
ข้อความที่มีการจัดรูปแบบกำหนดเองCATextLayer หรือ draw(_:)ขึ้นอยู่กับปริมาณ

ใช้ CAShapeLayer สำหรับการวาดรูปร่างเวกเตอร์พร้อมแอนิเมชัน — มันเร่งด้วยฮาร์ดแวร์และรองรับแอนิเมชัน path, strokeStart และ strokeEnd โดยไม่ต้องเรียก draw(_:)

การปรับประสิทธิภาพของ Custom UIView

ประสิทธิภาพของ Custom UIView ส่งผลโดยตรงต่อความราบรื่นของแอนิเมชันและประสบการณ์ผู้ใช้โดยรวม ปัญหาหลักเกิดจากการเรียก draw(_:) มากเกินไป การจัดวางวิวย่อยที่ไม่เหมาะสม และการขาดแคช

หลีกเลี่ยงการวาดซ้ำที่ไม่จำเป็น

ทุกครั้งที่เรียก setNeedsDisplay() จะทำให้เกิดการวาดวิวใหม่ทั้งหมด ใช้ setNeedsDisplay(_:) พร้อมสี่เหลี่ยมผืนผ้าที่เฉพาะเจาะจงหากการเปลี่ยนแปลงมีผลเพียงบางส่วนของวิว สำหรับคุณสมบัติ CALayer (backgroundColor, cornerRadius, shadow) ไม่จำเป็นต้องวาดใหม่ — พวกมันถูกอัปเดตในระดับ GPU

การแคชเนื้อหาที่เรนเดอร์แล้ว

หากเนื้อหาของ Custom UIView เปลี่ยนแปลงไม่บ่อย ให้เรนเดอร์มันครั้งเดียวใน UIGraphicsImageRenderer และบันทึกเป็น UIImage ในการวาดซ้ำครั้งต่อไป ให้ใช้ draw(at:) เพื่อแสดงภาพที่แคชไว้ — ซึ่งเร็วกว่าการเรนเดอร์ใหม่ผ่าน Core Graphics หลายสิบเท่า

swift
func renderToImage() -> UIImage {
    let renderer = UIGraphicsImageRenderer(size: bounds.size)
    return renderer.image { ctx in
        drawHierarchy(in: bounds, afterScreenUpdates: true)
    }
}

ใช้ shouldRasterize สำหรับเลเยอร์คงที่

คุณสมบัติ shouldRasterize บน CALayer เปิดใช้งานการแคชการแสดงผลบิตแมปของเลเยอร์ เปิดใช้งานสำหรับวิวคงที่ที่มีความโปร่งใสและเงา — ซึ่งจะลดภาระการประกอบภาพ ปิดใช้งานสำหรับวิวที่มีแอนิเมชัน: แคชจะถูกรีเซ็ตทุกครั้งที่เปลี่ยนแปลง และการทำราสเตอร์ไรซ์จะทำให้ประสิทธิภาพลดลงเท่านั้น

คำถามที่พบบ่อย

จำเป็นต้องแทนที่ draw(_:) ใน Custom UIView หรือไม่?

ไม่ draw(_:) จำเป็นสำหรับการวาดแบบกำหนดเองผ่าน Core Graphics เท่านั้น หากวิวประกอบด้วยวิวย่อยมาตรฐาน (UILabel, UIImageView) และใช้ CALayer ก็ไม่จำเป็นต้องแทนที่ draw(_:) — ซึ่งจะช่วยปรับปรุงประสิทธิภาพด้วยซ้ำ

จะเพิ่ม Custom UIView ไปยัง Storyboard ได้อย่างไร?

วาง UIView ปกติลงบน canvas ใน Identity Inspector ระบุคลาสของคุณในฟิลด์ Class หากคลาสถูกทำเครื่องหมายด้วย @IBDesignable การเปลี่ยนแปลงจะแสดงแบบเรียลไทม์โดยตรงใน Storyboard

ความแตกต่างระหว่าง init(frame:) และ init(coder:) คืออะไร?

init(frame:) ถูกเรียกเมื่อสร้างวิวด้วยโปรแกรม — คุณส่ง CGRect พร้อมตำแหน่งและขนาด init(coder:) ถูกเรียกเมื่อดีซีเรียลไลซ์จาก Storyboard หรือ XIB เพื่อการทำงานที่ถูกต้อง ต้องใช้ทั้งสองอย่าง มิฉะนั้นวิวของคุณจะล่มเมื่อโหลดจาก Interface Builder

ทำไม draw(_:) ถึงไม่ถูกเรียก?

สาเหตุที่พบบ่อยที่สุดคือวิวมี เฟรมเป็นศูนย์ (ความกว้างหรือความสูงเท่ากับศูนย์) ระบบจะไม่เรียก draw(_:) สำหรับวิวที่มีขนาดเป็นศูนย์ ตรวจสอบเฟรมใน layoutSubviews() และตรวจสอบให้แน่ใจว่าวิวถูกเพิ่มไปยังลำดับชั้นด้วยข้อจำกัดที่ถูกต้อง

จะอัปเดตเนื้อหาของ Custom UIView โดยไม่ต้องวาดใหม่ทั้งหมดได้อย่างไร?

ใช้ CALayer สำหรับคุณสมบัติที่รองรับแอนิเมชันบน GPU (position, opacity, transform) สำหรับการอัปเดตบางส่วนของ draw(_:) ให้ใช้ setNeedsDisplay(_:) พร้อม CGRect ของพื้นที่ที่เปลี่ยนแปลง — ระบบจะวาดเฉพาะพื้นที่ที่ระบุเท่านั้น ไม่ใช่ทั้งวิว

สรุป

  • Custom UIView — คลาสย่อยของ UIView ที่มีเมธอดที่ถูกแทนที่สำหรับการวาดแบบกำหนดเอง ใช้เมื่อคอมโพเนนต์ UIKit มาตรฐานไม่เพียงพอ
  • draw(_:) — เมธอดสำหรับการวาดแบบกำหนดเองผ่าน Core Graphics ถูกเรียกโดยอัตโนมัติโดยระบบ ห้ามเรียกโดยตรง
  • CALayer — แนวทางที่ต้องการสำหรับกราฟิกที่เร่งด้วย GPU (เงา การมุมมน รูปร่าง) ทางเลือกของ draw(_:) ที่มีประสิทธิภาพสูงกว่า
  • init(frame:) และ init(coder:) — ตัวเริ่มต้นที่จำเป็น; ต้องใช้ทั้งสองอย่างเพื่อการทำงานที่ถูกต้องจากโค้ดและ Storyboard
  • setNeedsDisplay() — สัญญาณให้ระบบเรียก draw(_:) อีกครั้ง; ใช้โอเวอร์โหลดกับ CGRect สำหรับการวาดซ้ำบางส่วน
  • @IBDesignable — คุณลักษณะสำหรับการแสดงตัวอย่างสดของ Custom UIView ใน Interface Builder ช่วยลดความซับซ้อนในการพัฒนาภาพ
  • สำหรับกราฟิกคงที่ ให้แคชผลลัพธ์ผ่าน UIGraphicsImageRenderer และวาดภาพที่เตรียมไว้ — ซึ่งจะลดภาระของ CPU

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

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

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

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