@State คือ Property Wrapper ใน SwiftUI สำหรับจัดการสถานะภายในเครื่องภายในวิวเดียว SwiftUI จะวาดวิวใหม่โดยอัตโนมัติทุกครั้งที่คุณสมบัติ @State เปลี่ยนแปลง ทำให้อินเทอร์เฟสตอบสนองได้โดยไม่ต้องเรียกการอัปเดตด้วยตนเอง ตาม Apple Developer Documentation (2025) @State ถูกแนะนำสำหรับชนิดข้อมูลอย่างง่ายและโครงสร้างที่เป็นของวิวเดียว @State เป็นวิธีที่ง่ายที่สุดในการเพิ่มการโต้ตอบให้กับอินเทอร์เฟส SwiftUI
ประเด็นสำคัญ
@State คือ Property Wrapper ที่สร้างไว้ใน SwiftUI ซึ่งช่วยให้วิวจัดเก็บและติดตามสถานะของตัวเองได้ เมื่อค่า @State เปลี่ยนแปลง SwiftUI จะวาดวิวใหม่โดยอัตโนมัติโดยเรียกคุณสมบัติ body อีกครั้ง นี่คือพื้นฐานของการเขียนโปรแกรมเชิงตอบสนองใน SwiftUI: นักพัฒนาประกาศสถานะ และเฟรมเวิร์กจัดการการซิงโครไนซ์อินเทอร์เฟส
@State สร้างพื้นที่จัดเก็บในฮีปที่จัดการโดย SwiftUI พื้นที่นี้ คงอยู่ — มันรอดจากการเริ่มต้นซ้ำของโครงสร้างวิวที่เกิดขึ้นในทุกการเรนเดอร์ SwiftUI ใช้ตัวระบุของวิว (สร้างจากตำแหน่งในลำดับชั้น) เพื่อผูกคุณสมบัติ @State กับวิวเฉพาะ ด้วยเหตุนี้ สถานะจึงไม่ถูกรีเซ็ตเมื่อวิวพ่อแม่ถูกอัปเดต
ข้อจำกัดที่สำคัญ: @State มีไว้สำหรับ ชนิดข้อมูลแบบค่า (struct, enum, ชนิดพื้นฐาน) เท่านั้น สำหรับชนิดข้อมูลแบบอ้างอิง (คลาส) ให้ใช้ @StateObject หรือ @ObservedObject หากคุณกำหนดคลาสให้กับคุณสมบัติ @State SwiftUI จะไม่สามารถตรวจจับการเปลี่ยนแปลงภายในออบเจกต์ได้ — มีเพียงการแทนที่การอ้างอิงทั้งหมด
SwiftUI ใช้งาน @State ผ่านกลไก Storage ภายใน แต่ละคุณสมบัติ @State จะได้รับเซลล์หน่วยความจำเฉพาะที่เก็บไว้ในคอนเทนเนอร์จัดเก็บพิเศษของวิว เมื่อเกิดการเขียนไปยัง wrappedValue SwiftUI จะแจ้งกราฟการพึ่งพาของมันผ่าน didSet เกี่ยวกับความจำเป็นในการวาดใหม่
struct ContentView: View {
@State private var name: String = "User"
@State private var isLoggedIn: Bool = false
var body: some View {
VStack {
Text("สวัสดี, \(name)")
Button(isLoggedIn ? "ออกจากระบบ" : "เข้าสู่ระบบ") {
isLoggedIn.toggle()
}
}
}
}
ในตัวอย่าง มีสองคุณสมบัติ @State: name (String) และ isLoggedIn (Bool) เมื่อ isLoggedIn.toggle() ถูกเรียก SwiftUI จะทำเครื่องหมาย ContentView ว่าต้องการการอัปเดตและเรียก body อีกครั้งในรอบการเรนเดอร์ถัดไป จุดสำคัญ: คุณสมบัติ @State ถูกประกาศด้วยตัวปรับแต่ง private เสมอ — นี่เป็นสัญญาณว่าสถานะเป็นของวิวปัจจุบันโดยเฉพาะและไม่ควรถูกเปลี่ยนแปลงจากภายนอกโดยตรง
สำหรับการสังเกตการเปลี่ยนแปลง SwiftUI ใช้ CurrentValueSubject จาก Combine แต่ละคุณสมบัติ @State จะสร้างผู้เผยแพร่ที่ซ่อนอยู่ซึ่งแจ้งระบบทุกครั้งที่มีการเปลี่ยนแปลง ซึ่งช่วยให้ SwiftUI วาดเฉพาะชุดวิวที่จำเป็นน้อยที่สุดเท่านั้น หลีกเลี่ยงการอัปเดตลำดับชั้นทั้งหมด
@State เหมาะที่สุดสำหรับ สถานะภายในเครื่องอย่างง่าย: ฟิลด์ข้อความในการค้นหา ธงบูลีนสำหรับหน้าต่างโมดัล สวิตช์การตั้งค่า ตัวนับ รายการที่ถูกเลือกในรายการ หากค่าใช้ในวิวเดียวและคอมโพเนนต์ลูกของมัน (ผ่าน @Binding) @State คือตัวเลือกที่ถูกต้อง สำหรับสถานะที่ต้องอยู่รอดเมื่อวิวปิด (เช่น ข้อมูลฟอร์ม) @State ก็ทำงานเช่นกันตราบใดที่วิวยังคงอยู่ในลำดับชั้น
อย่าใช้ @State สำหรับ สถานะทั่วโลก ของแอปพลิเคชัน การแคชข้อมูลเครือข่าย หรือออบเจกต์ที่ใช้ในหลายหน้าจอ @StateObject และ @EnvironmentObject ถูกออกแบบมาเพื่อวัตถุประสงค์เหล่านี้ นอกจากนี้ @State ไม่เหมาะสำหรับการจัดเก็บข้อมูลจำนวนมาก — ทุกการเปลี่ยนแปลงจะวาดวิวทั้งหมดใหม่
@Binding คือสะพานระหว่าง @State ในวิวพ่อแม่และวิวลูกที่ต้องการเปลี่ยนแปลงสถานะนั้น พ่อแม่ประกาศ @State และคอมโพเนนต์ลูกได้รับ Binding ผ่านการฉาย $ การเปลี่ยน Binding ในวิวลูกจะอัปเดต @State ในพ่อแม่โดยอัตโนมัติ — และในทางกลับกัน ซึ่งรับประกันการไหลของข้อมูลทางเดียวที่มีความสามารถในการตอบกลับ
struct ParentView: View {
@State private var text: String = ""
var body: some View {
ChildView(text: $text)
}
}
struct ChildView: View {
@Binding var text: String
var body: some View {
TextField("Enter text", text: $text)
}
}
ในลิสติ้ง ParentView เป็นเจ้าของ @State text และ ChildView ได้รับ $text เป็น Binding TextField ภายใน ChildView ผูกกับ Binding นี้ผ่าน text: $text เมื่อผู้ใช้พิมพ์ใน TextField ค่าจะเปลี่ยนแปลงใน ChildView ผ่าน Binding ซึ่งทำให้เกิดการอัปเดต @State ใน ParentView วิวทั้งสองจะถูกวาดใหม่ด้วยค่าใหม่
ข้อผิดพลาดที่พบบ่อยที่สุดคือการกำหนด คลาส ให้กับคุณสมบัติ @State หากคุณเขียน @State var model = MyClass() SwiftUI จะไม่สามารถติดตามการเปลี่ยนแปลงของคุณสมบัติภายในคลาสได้ — มีเพียงการแทนที่ออบเจกต์ สำหรับคลาส ให้ใช้ @StateObject เสมอ ปัญหาทั่วไปที่สองคือการประกาศ @State โดยไม่มีตัวปรับแต่ง private ซึ่งละเมิดหลักการของการห่อหุ้มสถานะ
การส่ง @State โดยตรง ไปยังวิวลูกโดยไม่มี $ เป็นข้อผิดพลาดทั่วไปอีกอย่างหนึ่ง หากคุณส่ง TextField(text: text) แทน TextField(text: $text) คอมโพเนนต์ลูกจะได้รับเพียงสตริง ไม่ใช่ Binding การเปลี่ยนแปลงข้อความใน TextField จะไม่ถูกซิงโครไนซ์กับ @State ของพ่อแม่ ใช้การฉาย $ เสมอเพื่อส่ง Binding
ข้อผิดพลาดที่สามคือ หลายคุณสมบัติ @State สำหรับข้อมูลที่เกี่ยวข้องกัน หากหลายค่าสร้างเป็นหนึ่งเดียวทางตรรกะ (เช่น ฟิลด์ฟอร์ม) ให้รวมพวกมันเป็นโครงสร้างเดียวกับ @State เดียว ซึ่งทำให้การส่งสถานะไปยังวิวลูกง่ายขึ้นและลดจำนวนทริกเกอร์การอัปเดตแต่ละรายการ
@State ถูกใช้ในโปรเจกต์ SwiftUI ส่วนใหญ่สำหรับการโต้ตอบพื้นฐาน พิจารณาตัวอย่างฟอร์มเข้าสู่ระบบที่ @State จัดการฟิลด์ข้อความและสถานะการโหลด รูปแบบนี้ปรากฏในทุกแอปพลิเคชัน — จากโน้ตง่าย ๆ ไปจนถึงโซลูชันองค์กรที่ซับซ้อน
struct LoginView: View {
@State private var email: String = ""
@State private var password: String = ""
@State private var isLoading: Bool = false
@State private var errorMessage: String?
var body: some View {
Form {
TextField("Email", text: $email)
SecureField("Password", text: $password)
Button("เข้าสู่ระบบ") {
login()
}.disabled(isLoading)
}
}
private func login() {
isLoading = true
// ดำเนินการคำขอเครือข่าย
}
}
ในตัวอย่าง มีสี่คุณสมบัติ @State: email และ password สำหรับฟิลด์ฟอร์ม isLoading สำหรับการบ่งชี้การโหลด และ errorMessage สำหรับการแสดงข้อผิดพลาด แต่ละคุณสมบัติจัดการส่วนของอินเทอร์เฟสอย่างอิสระ เมื่อ isLoading เปลี่ยนแปลง ปุ่มจะถูกปิดใช้งานโดยอัตโนมัติผ่าน disabled(isLoading) — โดยไม่ต้องอัปเดต UI ด้วยตนเอง
คำถามที่พบบ่อย
@State ถูกออกแบบมาสำหรับสถานะภายในเครื่องของวิวเฉพาะ ตัวปรับแต่ง private รับประกันว่าคอมโพเนนต์อื่นไม่สามารถเปลี่ยนแปลงมันโดยตรง ซึ่งจะทำลายการห่อหุ้ม สำหรับการเข้าถึงจากภายนอก ให้ใช้การฉาย $
ใช่ @State รองรับอาร์เรย์และดิกชันนารีเพราะ它们是ชนิดข้อมูลแบบค่า อย่างไรก็ตาม เมื่อสมาชิกของอาร์เรย์เปลี่ยนแปลง SwiftUI จะวาดวิวทั้งหมดใหม่ สำหรับลิสต์ขนาดใหญ่ @StateObject กับ @Published มีประสิทธิภาพมากกว่า
@State ทำงานได้อย่างถูกต้องกับชนิด Optional เมื่อ nil ถูกกำหนด SwiftUI จะตรวจจับการเปลี่ยนแปลงและวาดวิวใหม่ ซึ่งสะดวกสำหรับสถานะเช่น errorMessage: String? โดยที่ nil หมายถึงไม่มีข้อผิดพลาด
@State เก็บค่าไว้ตราบใดที่วิวยังคงอยู่ในลำดับชั้น หากวิวถูกลบออกจากลำดับชั้นและเพิ่มอีกครั้ง @State จะถูกเริ่มต้นใหม่ด้วยค่าเริ่มต้น สำหรับความคงทน ให้ใช้ @AppStorage
ใช่ ห่อการเปลี่ยนแปลงใน withAnimation: withAnimation(.easeInOut) { isExpanded.toggle() } SwiftUI จะทำให้การเปลี่ยนผ่านระหว่างสถานะอินเทอร์เฟสเก่าและใหม่เป็นแอนิเมชันด้วยชนิดแอนิเมชันที่ระบุ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม