คุณสมบัติ body เป็นองค์ประกอบหลักของโปรโตคอล View ใน SwiftUI ซึ่งกำหนดว่าเนื้อหาใดที่จะแสดงบนหน้าจอ ตาม Apple Developer Documentation, 2024 body เป็นข้อกำหนดบังคับเพียงอย่างเดียวของโปรโตคอล View และส่งคืนบางประเภทที่สอดคล้องกับโปรโตคอลเดียวกันนี้ SwiftUI เรียกใช้ body ทุกครั้งที่มีการเปลี่ยนแปลงสถานะเพื่อสร้างและเปรียบเทียบแผนผังองค์ประกอบใหม่
ประเด็นสำคัญ
body เป็นคุณสมบัติที่คำนวณได้ซึ่งเป็นข้อกำหนดบังคับเพียงอย่างเดียวของโปรโตคอล View ทุกโครงสร้างที่สอดคล้องกับ View ต้องใช้งาน body คุณสมบัตินี้ส่งคืนเนื้อหาที่ SwiftUI แสดงบนหน้าจอ — อาจเป็นข้อความ รูปภาพ ปุ่ม ภาชนะที่มีองค์ประกอบซ้อนกัน หรือประเภทอื่นใดที่สอดคล้องกับโปรโตคอล View
ลายเซ็นของ body คงที่เสมอ: var body: some View { get } ประเภทการส่งคืนคือ some View (ประเภททึบแสง) ไม่ใช่ประเภทที่เป็นรูปธรรม ซึ่งหมายความว่า Views ที่แตกต่างกันสามารถส่งคืนประเภทที่เป็นรูปธรรมที่แตกต่างกันใน body ได้ แต่คอมไพเลอร์ Swift จะกำหนดประเภทที่เป็นรูปธรรมสำหรับแต่ละการใช้งานในเวลาคอมไพล์
ตาม WWDC 2022 body คือจุดเริ่มต้นสำหรับคำอธิบายอินเทอร์เฟซแบบประกาศ ต่างจาก UIKit ที่คุณสร้างและกำหนดค่า UIView ตามคำสั่ง ใน SwiftUI คุณอธิบายสิ่งที่ควรแสดงในลักษณะประกาศ และ SwiftUI จะคำนวณวิธีใช้งานด้วยตัวเอง
body ควรทำงานเป็น ฟังก์ชันบริสุทธิ์ — ด้วยอินพุตเดียวกัน (คุณสมบัติของโครงสร้างและสถานะ) ควรส่งคืนแผนผัง View เดียวกัน ถ้า body ขึ้นอยู่กับสถานะที่เปลี่ยนแปลงได้ภายนอก (ตัวแปรส่วนกลาง UserDefaults โดยไม่มีตัวห่อ @AppStorage) พฤติกรรมจะคาดเดาไม่ได้และ SwiftUI อาจวาดหน้าจอใหม่ไม่ถูกต้อง
คุณสมบัติที่คำนวณได้ body ไม่ได้เก็บค่า — มันถูกคำนวณทุกครั้งที่เข้าถึง เมื่อ SwiftUI กำหนดว่าสถานะเปลี่ยนแปลงแล้ว มันจะสร้างโครงสร้าง View ใหม่และอ่านค่าใหม่ของ body เพื่อรับแผนผังองค์ประกอบปัจจุบันสำหรับการแสดงผล
struct CounterView: View {
@State private var count = 0
var body: some View {
VStack {
Text("ตัวนับ: \(count)")
.font(.largeTitle)
Button("เพิ่ม") {
count += 1
}
.padding()
.background(.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
}
ในตัวอย่างนี้ body ส่งคืน VStack ที่มีข้อความและปุ่มพร้อมตัวปรับแต่ง เมื่อกดปุ่ม คุณสมบัติ @State count จะเพิ่มขึ้น SwiftUI จะสร้างโครงสร้าง CounterView ใหม่และเรียกใช้ body อีกครั้งเพื่อรับแผนผังที่อัปเดตด้วยค่าข้อความใหม่
ตัวปรับแต่ง (.font, .padding, .background, .foregroundColor, .cornerRadius) ไม่ได้แก้ไข View ดั้งเดิม แต่ห่อหุ้มมันใน ModifiedContent — ประเภทใหม่ที่เพิ่มการปรับแต่ง ตัวปรับแต่งแต่ละตัวสร้างระดับการซ้อนอีกหนึ่งระดับ ซึ่งสำคัญที่ต้องพิจารณาสำหรับประสิทธิภาพ
some View ในประเภทการส่งคืนของ body ไม่ใช่แค่ธรรมเนียมแต่เป็นข้อกำหนดของคอมไพเลอร์ Swift ต้องการให้เส้นทางการส่งคืนทั้งหมดใน body มี ประเภทที่เป็นรูปธรรมเดียวกัน หากไม่มี @ViewBuilder คุณไม่สามารถส่งคืน Text ในสาขาหนึ่งและ Button ในอีกสาขาหนึ่ง — คอมไพเลอร์จะแจ้งข้อผิดพลาด
struct ConditionalView: View {
var isReady: Bool
@ViewBuilder
var body: some View {
if isReady {
Text("พร้อม")
.foregroundColor(.green)
} else {
ProgressView()
}
}
}
@ViewBuilder บน body อนุญาตให้ใช้ตรรกะแบบมีเงื่อนไข (if/else, switch) โดยไม่มีข้อผิดพลาดในการคอมไพล์ ViewBuilder จะห่อหุ้มสาขาต่าง ๆ โดยอัตโนมัติใน ConditionalContent — ประเภทพิเศษที่ซ่อนความแตกต่างของประเภทที่เป็นรูปธรรม นี่คือความสามารถสำคัญสำหรับการสร้างอินเทอร์เฟซแบบไดนามิก
หากไม่มี @ViewBuilder คอมไพเลอร์จะพยายามอนุมานประเภทเดียวสำหรับเส้นทางการส่งคืนทั้งหมด ถ้าประเภทแตกต่างกัน — จะเกิดข้อผิดพลาด นี่คือเหตุผลที่ SwiftUI ใช้ @ViewBuilder โดยนัยกับ body ในการประกาศ View แม้ว่าในโค้ดผู้ใช้คุณต้องเพิ่มคำอธิบายประกอบอย่างชัดเจนสำหรับเมธอดและคุณสมบัติที่กำหนดเองที่ส่งคืนหลาย Views
การใช้ some View แทนประเภทที่เป็นรูปธรรมไม่ได้ลดประสิทธิภาพ — คอมไพเลอร์รู้ประเภทที่แน่นอนในเวลาคอมไพล์และสร้างโค้ดโดยตรงโดยไม่ต้องจัดส่งแบบไดนามิก AnyView ในทางกลับกัน ใช้การลบประเภท (type erasure) ด้วยค่าใช้จ่ายในการห่อหุ้มในคอนเทนเนอร์อัตถิภาวนิยม
body ถูกเรียกโดย SwiftUI ในสามสถานการณ์หลัก: เมื่อ View ถูกแสดงครั้งแรก เมื่อ @State/@Binding/@ObservedObject/@StateObject เปลี่ยนแปลง และเมื่อ View แม่ส่งค่าใหม่ผ่านตัวเริ่มต้น SwiftUI อาจเรียกใช้ body เมื่อค่าสภาพแวดล้อม (@Environment) เปลี่ยนแปลง
ความถี่ของการเรียก body ไม่ควรทำให้คุณกังวล — SwiftUI ปรับการวาดใหม่ให้เหมาะสมผ่านกลไก เอกลักษณ์ แต่ละ View ในลำดับชั้นมีตัวระบุที่ไม่ซ้ำกัน ถ้าเอกลักษณ์และข้อมูลอินพุตไม่เปลี่ยนแปลง — body จะไม่ถูกเรียกแม้ว่า View แม่จะถูกวาดใหม่ ซึ่งทำได้ผ่านการเปรียบเทียบ Equatable และความเสถียรของโครงสร้าง
struct ParentView: View {
var body: some View {
ChildView(name: "Alice") // เอกลักษณ์ที่เสถียร
}
}
struct ChildView: View {
let name: String
var body: some View {
Text("สวัสดี, \(name)!")
}
}
ในตัวอย่างนี้ ถ้า ParentView ถูกวาดใหม่แต่ส่งค่า name เดียวกัน — ChildView.body จะไม่ถูกเรียก SwiftUI เปรียบเทียบข้อมูลอินพุตของโครงสร้าง และถ้าไม่เปลี่ยนแปลง จะข้ามการวาดใหม่ของคอมโพเนนต์ลูก นี่คือกลไก การแยกความแตกต่างของมุมมอง
มีข้อผิดพลาดหลายอย่างที่นำไปสู่การเรียก body โดยไม่คาดคิด: การใช้คลาสโดยไม่มี ObservableObject การส่งคลอเชอร์ที่สร้างขึ้นภายใน body (การสร้างคลอเชอร์แต่ละครั้งให้เอกลักษณ์ใหม่) และการใช้ EquatableView อย่างไม่ถูกต้อง ถ้า body ถูกเรียกบ่อยเกินไป — ตรวจสอบความเสถียรของเอกลักษณ์ของคอมโพเนนต์ลูกทั้งหมด
กฎข้อแรก: body ควรน้อยที่สุด ย้ายตรรกะที่ซับซ้อนไปยังคุณสมบัติที่คำนวณได้หรือเมธอดแยกต่างหากที่ส่งคืน View สิ่งนี้ช่วยเพิ่มความสามารถในการอ่านและช่วยให้ SwiftUI ระบุได้แม่นยำยิ่งขึ้นว่าส่วนใดของลำดับชั้นที่เปลี่ยนแปลง แบ่ง body ขนาดใหญ่ออกเป็นคอมโพเนนต์ย่อยที่มีขอบเขตความรับผิดชอบที่ชัดเจน
กฎข้อที่สอง: อย่าใช้ body เพื่อทำงาน การโหลดข้อมูล การดำเนินการเครือข่าย การเขียนฐานข้อมูล — ทั้งหมดนี้ควรเกิดขึ้นภายนอก body ในงาน (tasks) ตัวปรับแต่ง onChange หรือผ่าน ObservableObject body มีไว้สำหรับการประกาศอินเทอร์เฟซเท่านั้น
กฎข้อที่สาม: ใช้คุณสมบัติ EquatableView หรือโปรโตคอล Equatable ที่กำหนดเองสำหรับ Views ถ้าการเปรียบเทียบโครงสร้างมาตรฐานไม่เพียงพอ สิ่งนี้ช่วยให้คุณบอก SwiftUI อย่างชัดเจนว่าเมื่อใดที่ View ลูกต้องการการวาดใหม่และหลีกเลี่ยงการเรียก body ที่ไม่จำเป็น
กฎข้อที่สี่: ถ้า body มีการคำนวณที่ซับซ้อน (การจัดรูปแบบ การกรอง การเรียงลำดับ) — ใช้ @State สำหรับการแคชผลลัพธ์หรือย้ายการคำนวณไปยังเมธอดแยกที่เรียกจาก onChange การคำนวณซ้ำใน body ทุกครั้งที่อัปเดตสถานะเป็นสาเหตุทั่วไปของภาพเคลื่อนไหวช้า
กฎข้อที่ห้า: สำหรับรายการ (List, ForEach) ให้มั่นใจว่ามี ตัวระบุที่เสถียร ผ่านพารามิเตอร์ id หากไม่มีเอกลักษณ์ที่เสถียร ForEach จะสร้างองค์ประกอบทั้งหมดใหม่เมื่อมีการเปลี่ยนแปลงใด ๆ โดยเรียก body สำหรับแต่ละองค์ประกอบ แม้ว่าจะมีเพียงองค์ประกอบเดียวที่เปลี่ยนแปลง
คำถามที่พบบ่อย
body เป็นคุณสมบัติที่คำนวณได้ของโปรโตคอล View ที่ส่งคืนเนื้อหาสำหรับแสดง เป็นข้อกำหนดบังคับเพียงอย่างเดียวของโปรโตคอล ประเภทการส่งคืนคือ some View ซึ่งช่วยให้ SwiftUI ปรับลำดับชั้นให้เหมาะสมในเวลาคอมไพล์
ได้ SwiftUI เรียก body ทุกครั้งที่มีการเปลี่ยนแปลงสถานะ (@State, @Binding, @ObservedObject) หรือข้อมูลอินพุต นี่เป็นพฤติกรรมปกติของเฟรมเวิร์กแบบประกาศ SwiftUI ปรับความถี่การเรียกให้เหมาะสมผ่านกลไกเอกลักษณ์และการเปรียบเทียบ Equatable
some View เป็นประเภททึบแสงที่ซ่อนการใช้งานที่เป็นรูปธรรม คอมไพเลอร์กำหนดประเภทในเวลาคอมไพล์เพื่อให้มั่นใจถึงประสิทธิภาพการเรียกโดยตรง สิ่งนี้ให้ความยืดหยุ่น: คุณสามารถเปลี่ยนประเภทการส่งคืนได้โดยไม่ต้องเปลี่ยนลายเซ็น
ไม่ body ไม่สามารถเป็นทางเลือกได้ — ประเภทการส่งคืน some View ไม่อนุญาตให้เป็น nil ถ้าคุณต้องการซ่อนองค์ประกอบตามเงื่อนไข ให้ใช้ตรรกะแบบมีเงื่อนไขภายใน @ViewBuilder หรือส่งคืน EmptyView ซึ่งไม่ใช้พื้นที่ในลำดับชั้น
ตัวปรับแต่งแต่ละตัวสร้างชั้น ModifiedContent ใหม่ เพิ่มความลึกของลำดับชั้น สำหรับหน้าจอส่วนใหญ่ (สูงสุด 50 ตัวปรับแต่ง) ผลกระทบไม่มีนัยสำคัญ จำนวนตัวปรับแต่งที่มากเกินไป (หลายร้อย) อาจทำให้การเปรียบเทียบช้าลง จัดกลุ่มตัวปรับแต่งที่เกี่ยวข้องในส่วนขยายที่กำหนดเอง
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม