Layer เป็นนามธรรมของเนื้อหากราฟิกที่จัดการการแสดงผลด้วยภาพขององค์ประกอบอินเทอร์เฟซในแอปพลิเคชันมือถือ แตกต่างจาก UIView ตรงที่ layer ไม่ประมวลผลเหตุการณ์การสัมผัสและไม่มีส่วนร่วมใน Auto Layout — หน้าที่เดียวของมันคือการเรนเดอร์ แอนิเมชัน และการรวมพิกเซล ตาม Apple QuartzCore Documentation, 2025 ทุก UIView ใน iOS มี CALayer ที่เกี่ยวข้องซึ่งจัดการการวาดและแอนิเมชันจริง ๆ การทำความเข้าใจโครงสร้างของเลเยอร์ช่วยให้นักพัฒนาสามารถควบคุมประสิทธิภาพการเรนเดอร์ในระดับพิกเซลแต่ละพิกเซลได้
ประเด็นสำคัญ
Layer เป็นวัตถุระบบกราฟิกระดับต่ำที่เก็บภาพบิตแมปของส่วนหน้าจอและจัดการแอตทริบิวต์ภาพของมัน: ตำแหน่ง ขนาด การหมุน ความทึบ เงา และสี ใน iOS ทุก UIView มี CALayer ในตัวที่เข้าถึงได้ผ่านคุณสมบัติ layer นักพัฒนาสามารถทำงานโดยตรงกับ layer โดยข้าม UIView เพื่อการควบคุมการเรนเดอร์ที่ละเอียด
สถาปัตยกรรมเลเยอร์เป็นไปตามรูปแบบ Model-View-Controller โดยที่ CALayer ทำหน้าที่เป็น Model — มันเก็บสถานะของคุณสมบัติภาพ Core Animation เป็น Controller ที่จัดการการเปลี่ยนแอนิเมชันระหว่างสถานะ View (UIView) เป็นตัวห่อหุ้มเสริมที่เพิ่มการจัดการการสัมผัสและการมีส่วนร่วมใน Auto Layout
ตาม Apple WWDC 2024 ไพพ์ไลน์การเรนเดอร์สมัยใหม่ของ iOS ใช้ Metal สำหรับการรวมเลเยอร์ แต่ละ CALayer จะถูกเรนเดอร์ในบัฟเฟอร์แยกต่างหาก หลังจากนั้น Core Animation จะรวมบัฟเฟอร์ทั้งหมดเป็นภาพสุดท้าย โดยคำนึงถึงความทึบและโหมดการผสม
ใน Android สิ่งที่คล้ายกับเลเยอร์คือ View และ Drawable พื้นหลัง แต่การทำงานโดยตรงกับเลเยอร์กราฟิกสามารถทำได้ผ่าน Canvas และ RenderNode ใน Android 10+ การทำความเข้าใจแนวคิดของเลเยอร์เป็นสิ่งสำคัญสำหรับการปรับแต่งการเรนเดอร์บนทั้งสองแพลตฟอร์ม
ความแตกต่างหลักอยู่ที่พื้นที่ความรับผิดชอบ View จัดการอินพุตของผู้ใช้ (การสัมผัส ท่าทาง) การวางตำแหน่ง (Auto Layout, frame) และวงจรชีวิต Layer รับผิดชอบเฉพาะการนำเสนอด้วยภาพ: การเรนเดอร์เนื้อหา แอนิเมชันคุณสมบัติ และการรวมกับเลเยอร์อื่น ๆ
การแยกนี้ช่วยให้สามารถแคชการแสดงบิตแมปของเลเยอร์โดยอิสระจาก View หากเลเยอร์ไม่เปลี่ยนแปลง Core Animation จะใช้เฟรมที่แคชไว้โดยไม่เรียก drawRect สำหรับองค์ประกอบแบบคงที่ สิ่งนี้ให้ประสิทธิภาพที่เพิ่มขึ้นอย่างมากโดยไม่ต้องเปลี่ยนแปลงโค้ด
iOS มีลำดับชั้นที่หลากหลายของคลาสที่สืบทอดจาก CALayer แต่ละคลาสย่อยถูกปรับให้เหมาะสมสำหรับสถานการณ์เฉพาะ: การแสดงข้อความ กราฟิกเวกเตอร์ การไล่ระดับสี หรือการแปลง 3 มิติ การเลือกประเภทเลเยอร์ที่ถูกต้องส่งผลโดยตรงต่อประสิทธิภาพการเรนเดอร์
ใน Android แนวคิดของเลเยอร์ถูกนำไปใช้ผ่าน RenderNode, ViewLayer และ HardwareRenderer ตั้งแต่ Android 5.0 (API 21) แต่ละ View จะถูกเรนเดอร์ในเลเยอร์เร่งด้วยฮาร์ดแวร์ของตัวเอง ทำให้สามารถทำแอนิเมชันได้โดยไม่ต้องเรียก onDraw
ความยืดหยุ่นเพิ่มเติมใน iOS มีให้โดย CAReplicatorLayer และ CAEmitterLayer อย่างแรกใช้สำหรับสร้างรูปแบบที่ซ้ำกัน อย่างที่สองสำหรับระบบอนุภาค ทั้งสองทำงานเฉพาะบน GPU ทำให้สามารถสร้างเอฟเฟกต์ภาพที่ซับซ้อนได้โดยไม่สูญเสียประสิทธิภาพ
ลำดับชั้นของเลเยอร์ ใน iOS เป็นไปตามโครงสร้างต้นไม้: แต่ละ CALayer สามารถมีเลเยอร์ย่อยหลายชั้น การแปลงทั้งหมดที่ใช้กับเลเยอร์หลัก — การปรับขนาด การหมุน การเลื่อน — จะถูกนำไปใช้กับองค์ประกอบลูกโดยอัตโนมัติ สิ่งนี้ช่วยให้มั่นใจถึงความสอดคล้องทางภาพระหว่างแอนิเมชัน
การรวมฉากดำเนินการโดย Core Animation ตามลำดับต่อไปนี้: ขั้นแรกเรนเดอร์พื้นหลัง จากนั้นแต่ละเลเยอร์ลูกตามลำดับจากล่างขึ้นบน สำหรับแต่ละเลเยอร์ Core Animation จะตรวจสอบคุณสมบัติความทึบ มาสก์ และโหมดการผสม จากนั้นรวมเป็นเฟรมสุดท้าย
ตาม Apple Documentation การรวมเลเยอร์ที่มี opaque = true และไม่มีช่องอัลฟ่าจะดำเนินการโดยไม่ต้องผ่านเพิ่มเติม — GPU เพียงคัดลอกพิกเซลทับเลเยอร์ก่อนหน้า หากเลเยอร์มีความโปร่งใส GPU จะทำการผสมอัลฟ่า ซึ่งต้องใช้ทรัพยากรการคำนวณเพิ่มเติม
ใน Android การรวมเลเยอร์ดำเนินการผ่าน SurfaceFlinger — บริการระบบที่รับบัฟเฟอร์จากแต่ละแอปพลิเคชันและรวมตามลำดับ z แต่ละ Window ใน Android เป็น Surface แยกต่างหากที่สามารถมีเลเยอร์กราฟิกหลายชั้น
CALayer.mask — คุณสมบัติที่อนุญาตให้ใช้มาสก์รูปทรงตามต้องการกับเลเยอร์ มาสก์สามารถเป็น CALayer อื่นใดก็ได้ — ตัวอย่างเช่น CAShapeLayer ที่มีเส้นทางวงกลมหรือ CAGradientLayer สำหรับสร้างการเปลี่ยนความทึบที่นุ่มนวล การใช้มาสก์เพิ่มภาระให้กับ GPU เนื่องจากต้องผ่านการเรนเดอร์เพิ่มเติมเพื่อคำนวณค่าอัลฟ่าสำหรับแต่ละพิกเซล
สำหรับการตัดรูปสี่เหลี่ยมอย่างง่าย แนะนำให้ใช้ cornerRadius และ masksToBounds แตกต่างจากมาสก์ผ่านคุณสมบัติ mask ตรงที่ cornerRadius ถูกประมวลผลโดยฮาร์ดแวร์ในขั้นตอนการแรสเตอร์และไม่ต้องผ่านเพิ่มเติม
shouldRasterize เป็นคุณสมบัติหลักของ CALayer สำหรับการปรับแต่งประสิทธิภาพ เมื่อ shouldRasterize ถูกตั้งค่าเป็น true Core Animation จะเรนเดอร์เลเยอร์และเลเยอร์ย่อยทั้งหมดลงในบัฟเฟอร์นอกหน้าจอแยกต่างหาก ในเฟรมถัดไป ภาพบิตแมปที่แคชไว้จะถูกใช้แทนการเรนเดอร์ใหม่
การแคชมีประสิทธิภาพสำหรับเลเยอร์ที่คงที่หรือเปลี่ยนแปลงน้อยครั้ง: เงา การไล่ระดับสี ข้อความที่มีมุมโค้งมน อย่างไรก็ตาม สำหรับเลเยอร์ที่อัปเดตบ่อยครั้ง (แอนิเมชัน วิดีโอ การเลื่อน) การแรสเตอร์อาจลดประสิทธิภาพเนื่องจากแต่ละเฟรมต้องสร้างแคชใหม่
จากการวิจัยของ Objc.io การใช้ shouldRasterize อย่างถูกต้องบน iPad Pro ช่วยลดเวลาในการเรนเดอร์หน้าจอที่ซับซ้อนจาก 25 ms เหลือ 8 ms — มากกว่าสามเท่า เงื่อนไขสำคัญคือเลเยอร์ไม่ควรเปลี่ยนแปลงบ่อยกว่า 1 ครั้งทุก 3–5 เฟรม
ตัวอย่างแรกสาธิตการตั้งค่าคุณสมบัติภาพพื้นฐานของ CALayer ใน Swift — การโค้งมนของมุม เงา และเส้นขอบ:
import UIKit
class StyledView: UIView {
override func awakeFromNib() {
super.awakeFromNib()
layer.cornerRadius = 12
layer.masksToBounds = false
layer.shadowColor = UIColor.darkGray.cgColor
layer.shadowOpacity = 0.3
layer.shadowOffset = CGSize(width: 0, height: 4)
layer.shadowRadius = 8
}
}
ตัวอย่างที่สอง — การสร้างแอนิเมชันเลเยอร์ใน Swift โดยใช้ CABasicAnimation คุณสมบัติ position ถูกทำให้เคลื่อนไหวโดยไม่ต้องใช้ UIView:
let animation = CABasicAnimation(keyPath: "position")
animation.fromValue = NSValue(cgPoint: CGPoint(x: 0, y: 0))
animation.toValue = NSValue(cgPoint: CGPoint(x: 150, y: 300))
animation.duration = 1.0
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
targetLayer.add(animation, forKey: "moveAnimation")
ตัวอย่างที่สาม — การทำงานกับ RenderNode ใน Kotlin บน Android RenderNode เป็นสิ่งที่คล้าย CALayer โดยตรง ช่วยให้สามารถดำเนินการกับเลเยอร์กราฟิกในระดับ Canvas:
import android.graphics.renderer.RenderNode
fun createLayer(): RenderNode {
val node = RenderNode("customLayer")
node.setPosition(0, 0, 300, 200)
node.setScaleX(1.5f)
node.setScaleY(1.5f)
val canvas = node.beginRecording()
canvas.drawColor(android.graphics.Color.BLUE)
node.endRecording()
return node
}
คำถามที่พบบ่อย
UIView เป็นตัวห่อหุ้มรอบ CALayer ที่เพิ่มการจัดการการสัมผัสและการมีส่วนร่วมใน Auto Layout Layer จัดการเฉพาะการเรนเดอร์และแอนิเมชัน คุณสามารถทำงานโดยตรงกับ CALayer ได้ แต่จำเป็นต้องใช้ UIView สำหรับการประมวลผลเหตุการณ์อินพุต
shouldRasterize เปิดใช้งานการแคชเลเยอร์ในบัฟเฟอร์นอกหน้าจอแยกต่างหาก ใช้สำหรับองค์ประกอบที่คงที่หรือเปลี่ยนแปลงน้อยครั้งที่มีเงาหรือการประกอบที่ซับซ้อน อย่าใช้สำหรับเลเยอร์ที่เคลื่อนไหวบ่อยครั้ง — การสร้างแคชใหม่จะมีค่าใช้จ่ายสูงกว่าการเรนเดอร์โดยตรง
ตั้งค่าคุณสมบัติ cornerRadius สำหรับการโค้งมนของมุมและ masksToBounds = true สำหรับการตัดเนื้อหาตามขอบเขตของเลเยอร์ สำหรับเงา masksToBounds ต้องเป็น false มิฉะนั้นเงาจะถูกตัด — ในกรณีนี้ ให้ใช้เลเยอร์แยกต่างหากสำหรับเงา
ใช่ RenderNode ใน Android 10+ มีฟังก์ชันการทำงานที่คล้ายกัน: การจัดการตำแหน่ง ขนาด การหมุน และความทึบในระดับเลเยอร์กราฟิก Canvas และ HardwareRenderer จัดเตรียมการเรนเดอร์บน GPU อย่างไรก็ตาม ในเชิงสถาปัตยกรรม Android ไม่ได้แยก View และเลเยอร์อย่างเคร่งครัดเหมือน iOS
เงาใน CALayer คำนวณผ่าน shadowPath หรือโดยอัตโนมัติตามช่องอัลฟ่า การคำนวณอัตโนมัติต้องวนผ่านทุกพิกเซลของเลเยอร์ ซึ่งมีค่าใช้จ่ายสูง ระบุ shadowPath ที่ชัดเจน — สี่เหลี่ยมผืนผ้าหรือ UIBezierPath — สิ่งนี้ช่วยให้ GPU คำนวณเงาได้โดยไม่ต้องวนผ่านเนื้อหา
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ