@Binding คือ Property Wrapper ใน SwiftUI ที่สร้างการอ้างอิงไปยังข้อมูลที่เป็นของคอมโพเนนต์อื่น Binding ไม่ได้เก็บค่าด้วยตัวเอง — มันเพียงให้การเข้าถึงแหล่งความจริงที่มีอยู่ผ่านการฉายภาพ $ ตาม เอกสารสำหรับนักพัฒนา Apple (2025) Binding ให้การสื่อสารแบบตอบสนองสองทิศทางระหว่างวิวพ่อแม่และวิวลูกโดยไม่ต้องเป็นเจ้าของข้อมูลโดยตรง @Binding เป็นกลไกหลักในการส่งสถานะที่เปลี่ยนแปลงได้ลงไปตามลำดับชั้น
ประเด็นสำคัญ
@Binding คือ Property Wrapper ที่สร้างการเชื่อมต่อแบบสองทิศทางระหว่างคุณสมบัติที่เก็บในวิวพ่อแม่และคอมโพเนนต์ลูก ความแตกต่างหลักระหว่าง Binding และ @State: Binding ไม่ได้เป็นเจ้าของข้อมูล มันอ่านและเขียนค่าผ่านแหล่งที่แท้จริง — @State, @StateObject หรือ Binding อื่นในวิวพ่อแม่เท่านั้น หากไม่มี Binding วิวลูกจะไม่สามารถแก้ไขสถานะของบรรพบุรุษได้หากไม่มีคอลแบ็กหรือตัวแทน
Binding ถูกนำไปใช้เป็นโครงสร้างที่มีสองคุณสมบัติ: wrappedValue (ค่าปัจจุบัน) และ projectedValue (Binding เอง เข้าถึงได้ผ่าน $) เมื่อวิวลูกเปลี่ยน wrappedValue ผ่าน Binding SwiftUI จะส่งการเปลี่ยนแปลงไปยังแหล่งข้อมูลและวาดวิวที่ต้องพึ่งพาทั้งหมดใหม่ สิ่งนี้เกิดขึ้นแบบซิงโครนัสภายในรอบการอัปเดตปัจจุบัน
คุณสมบัติที่สำคัญ: @Binding ไม่จำกัดอยู่ที่การส่งผ่านระดับเดียว Binding สามารถส่งผ่านหลายระดับลำดับชั้น — คอมโพเนนต์ลูกแต่ละตัวได้รับข้อมูลอ้างอิงไปยังแหล่งข้อมูลเดียวกัน การเปลี่ยนแปลงในระดับใดก็ตามจะทำให้เกิดการอัปเดตครั้งเดียวของวิวที่ผูกไว้ทั้งหมด
กลไกการผูกแบบสองทิศทางผ่าน @Binding สร้างขึ้นบนการฉายภาพของ Property Wrapper เมื่อวิวพ่อแม่ประกาศ @State var value: T SwiftUI จะสร้างการฉายภาพ $value ชนิด Binding<T> โดยอัตโนมัติ การส่ง $value ไปยังคอมโพเนนต์ลูกที่มี @Binding var value: T คุณจะเชื่อมต่อวิวทั้งสองกับเซลล์หน่วยความจำเดียวกัน การเขียนใดๆ ผ่าน Binding ในวิวลูกจะทำให้เกิดการวาดคอมโพเนนต์ทั้งสองใหม่
struct SliderContainer: View {
@State private var value: Double = 0.5
var body: some View {
VStack {
Text("Value: \(value)")
SliderView(value: $value)
}
}
}
struct SliderView: View {
@Binding var value: Double
var body: some View {
Slider(value: $value, in: 0...1)
}
}
ในตัวอย่าง SliderContainer เป็นเจ้าของ @State value และ SliderView ได้รับ Binding ผ่าน $value Slider ภายใน SliderView ผูกกับ Binding นี้ เมื่อลากแถบเลื่อน Slider จะเปลี่ยนค่าผ่าน Binding ซึ่งอัปเดต @State ใน SliderContainer โดยอัตโนมัติ และวิวทั้งสองแสดงจำนวนปัจจุบัน ทั้งลูกโซ่ทำงานโดยไม่มีคอลแบ็กหรือการแจ้งเตือนแม้แต่ครั้งเดียว
ในการสร้าง Binding จาก @StateObject หรือ @ObservedObject จะใช้การฉายภาพเดียวกัน: $object.property ให้ Binding<PropertyType> ซึ่งอนุญาตให้ส่งคุณสมบัติแต่ละรายการของ ObservableObject ไปยังวิวลูกโดยไม่ต้องส่งออบเจ็กต์ทั้งหมด วิธีการนี้ให้การเชื่อมต่อที่แคบกว่าและป้องกันการวาดใหม่ที่ไม่จำเป็น
ก่อน SwiftUI วิธีมาตรฐานในการส่งการเปลี่ยนแปลงขึ้นไปตามลำดับชั้นคือผ่าน คอลแบ็ก และตัวแทน: วิวพ่อแม่ส่งคลosure และคอมโพเนนต์ลูกเรียกใช้เมื่อมีการเปลี่ยนแปลง @Binding นำเสนอทางเลือกด้วยโค้ดที่น้อยกว่าและไวยากรณ์ที่ประกาศมากกว่า แทนที่จะส่งคลosure เสร็จสิ้น คุณเพียงแค่ส่ง $stateValue
| เกณฑ์ | @Binding | คอลแบ็ก |
|---|---|---|
| โค้ด | คำอธิบายประกอบหนึ่งตัว + $ | คลosure + การเรียก |
| หลายระดับ | อัตโนมัติ | ลูกโซ่คลosure |
| การทดสอบ | Binding(value:constant) | คลosure จำลอง |
| ความอ่านง่าย | สูง | ปานกลาง |
| ความยืดหยุ่น | เฉพาะข้อมูล | ตรรกะใดๆ |
ใช้ @Binding เมื่อวิวลูกต้องการเพียงอ่านและแก้ไขค่า หากจำเป็นต้องมีผลข้างเคียงเมื่อเปลี่ยนแปลง (การตรวจสอบ การบันทึก คำขอเครือข่าย) ให้รวม Binding กับคอลแบ็ก: ส่ง Binding สำหรับข้อมูลและคลosure สำหรับเหตุการณ์ ตัวอย่างเช่น TextField สามารถผูกกับ Binding ในขณะที่ onChange เรียกใช้การตรวจสอบ
@Binding ใช้ในหลายสถานการณ์ทั่วไป อย่างแรก — ควบคุมแบบกำหนดเอง: สวิตช์ แถบเลื่อน ตัวเลือกสี และองค์ประกอบเชิงโต้ตอบอื่นๆ ยอมรับ Binding สำหรับการซิงโครไนซ์แบบสองทิศทาง อย่างที่สอง — หน้าต่างโมดอล: แฟล็กการแสดงชีตถูกส่งเป็น Binding ทำให้วิวลูกสามารถปิดตัวเองผ่าน presentationMode หรือการตั้งค่าโดยตรง
รูปแบบที่สาม — ฟอร์มที่มีการแยก หากฟอร์มประกอบด้วยหลายฟิลด์ แต่ละฟิลด์สามารถแยกออกเป็นคอมโพเนนต์แยกต่างหากที่ยอมรับ Binding สำหรับค่าของมัน สิ่งนี้ทำให้การทดสอบและการใช้ฟิลด์ซ้ำระหว่างฟอร์มต่างๆ ง่ายขึ้น คอมโพเนนต์พ่อแม่ยังคงเป็นเจ้าของแต่เพียงผู้เดียวของโมเดลฟอร์มทั้งหมด
struct FormField: View {
let title: String
@Binding var text: String
var body: some View {
VStack(alignment: .leading) {
Text(title).font(.caption)
TextField("Enter \(title.lowercased())", text: $text)
.textFieldStyle(.roundedBorder)
}
}
}
คอมโพเนนต์ FormField ยอมรับหัวข้อและ Binding ไปยังสตริง มันแสดงป้ายกำกับและ TextField ที่ผูกกับ Binding ที่ส่งผ่าน ฟอร์มใดๆ สามารถใช้ FormField หลายครั้งโดยส่ง $property สำหรับแต่ละฟิลด์ ซึ่งลดการซ้ำซ้อนของมาร์กอัปและรวมศูนย์การจัดรูปแบบฟิลด์ข้อความ
SwiftUI อนุญาตให้สร้าง Binding ด้วยตนเองผ่านตัวเริ่มต้น Binding(get:set:) ซึ่งมีประโยชน์เมื่อต้องเพิ่มตรรกะเมื่ออ่านหรือเขียนค่า ตัวอย่างเช่น สามารถสร้าง Binding ที่จัดรูปแบบตัวเลขก่อนบันทึก หรือ Binding ที่ซิงโครไนซ์ค่ากับเซิร์ฟเวอร์ระยะไกลทุกครั้งที่มีการเปลี่ยนแปลง
struct ValidatedField: View {
@State private var email: String = ""
var emailBinding: Binding<String> {
.init(
get: { email },
set: { email = $0.lowercased().trimmingCharacters(in: .whitespaces) }
)
}
var body: some View {
TextField("Email", text: emailBinding)
}
}
ในรายการ emailBinding แบบกำหนดเองจะแปลงข้อความเป็นตัวพิมพ์เล็กและตัดช่องว่างโดยอัตโนมัติทุกครั้งที่มีการเปลี่ยนแปลง TextField ใช้ Binding นี้แทนการผูกโดยตรงกับ $email วิธีการนี้รวมศูนย์การตรวจสอบและการแปลงข้อมูลภายใน Binding โดยไม่ทำให้โค้ดรกด้วยตัวจัดการ onChange
ข้อผิดพลาดแรกและพบบ่อยที่สุดคือ การส่งค่าแทน Binding หากคอมโพเนนต์ลูกประกาศ @Binding var text: String และวิวพ่อแม่ส่ง text (โดยไม่มี $) คอมไพเลอร์จะแสดงข้อผิดพลาด: Cannot convert value of type 'String' to expected argument type 'Binding<String>' วิธีแก้ปัญหา — ใช้คำนำหน้า $ เสมอเมื่อส่ง: $text
ข้อผิดพลาดที่สอง — Binding บนข้อมูลแบบอ่านอย่างเดียว หากวิวลูกต้องการเพียงอ่านค่า อย่าใช้ @Binding — แค่ let ธรรมดาหรือ @State จากวิวพ่อแม่ก็เพียงพอ Binding หมายถึงความสามารถในการเขียน และสิทธิ์การแก้ไขที่มากเกินไปทำให้การดีบักซับซ้อนและละเมิดหลักการสิทธิพิเศษน้อยที่สุด
ปัญหาที่สาม — Binding.constant ในโปรดักชัน Binding.constant(value) สร้างการผูกจำลองที่ไม่มีการตอบกลับ — การเปลี่ยนแปลงจะถูกละเว้น ใช้ constant สำหรับการสร้างต้นแบบและการแสดงตัวอย่าง (Xcode Previews) เท่านั้น แต่อย่าใช้ในโค้ดจริง สำหรับการทดสอบ ใช้ Binding(get:set:) ที่มีพฤติกรรมควบคุม
คำถามที่พบบ่อย
@State เป็นเจ้าของข้อมูลและจัดการการจัดเก็บในฮีป @Binding อ้างอิงเฉพาะสถานะที่มีอยู่โดยไม่เป็นเจ้าของ @State เป็นส่วนตัวเสมอ @Binding เป็นพารามิเตอร์อินพุตของวิวลูก
ได้ ผ่านตัวเริ่มต้น Binding(get:set:) หรือ Binding.constant(value) Binding ยังสามารถได้รับจาก @StateObject ผ่านการฉายภาพ $object.$property และจาก Publisher ผ่าน Binding(get:set:) ภายใน Subscribe
@Binding ถูกส่งผ่านลูกโซ่: คอมโพเนนต์กลางแต่ละตัวประกาศ @Binding และส่งต่อผ่าน $ ทุกระดับอ้างอิงถึงแหล่งข้อมูลเดียวกันในวิวราก
Binding.constant สร้างตัวหุ้มเงียบ — ตัวตั้งค่าละเว้นค่าใหม่ มันมีไว้สำหรับการสร้างต้นแบบและ SwiftUI Previews เท่านั้นซึ่งไม่ต้องการการตอบกลับจากคอมโพเนนต์ลูก
ได้ Binding<T?> รองรับ หากคุณส่ง Binding<String?> วิวลูกจะสามารถตั้งค่า nil ได้ สะดวกสำหรับฟิลด์ฟอร์มที่เป็นตัวเลือกหรือสถานะที่มีตัวเลือกรีเซ็ต
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม