@Environment ใน SwiftUI เป็น property wrapper สำหรับอ่านค่าจากสภาพแวดล้อมของระบบ ซึ่งแจกจัดโดยอัตโนมัติผ่านลำดับชั้น View ส่วนประกอบนี้ให้การเข้าถึงโครงสี ตำแหน่งที่อยู่ ขนาดแบบอักษร managedObjectContext และพารามิเตอร์ระบบอื่นๆ อีกมากมาย ตาม เอกสาร Apple Developer (2025), @Environment รับประกันว่าการเปลี่ยนแปลงค่าสภาพแวดล้อมใดๆ จะทำให้เกิดการเรียกโหมาระบายใหม่ของ View ทั้งหมดที่สมัครสมาชิก ซึ่งให้การอัพเดตอินเตอร์เฟสแบบรีแอคทีฟโดยไม่ต้องเรียกด้วยตนเอง
ประเด็นสำคัญ
@Environment เป็น SwiftUI property wrapper ที่ออกแบบมาสำหรับอ่านค่าจากสภาพแวดล้อมของระบบ สภาพแวดล้อมเป็นตัวเก็บค่าแบบลำดับชั้นที่ SwiftUI แจกจัดโดยอัตโนมัติจาก View แม่ไปยัง View ลูก แต่ละค่าสภาพแวดล้อมจะถูกระบุโดยคีย์ — ชนิดที่ปฏิบัติตามโปรโตคอล EnvironmentKey
กลไกของสภาพแวดล้อมมีลักษณะคล้ายกับ dependency injection ในระดับ framework: ระบบจัดหาชุดค่าที่กำหนดไว้ล่วงหน้า — โครงสี (สว่าง/มืด), ตำแหน่งที่อยู่, ขนาดแบบอักษร, managedObjectContext สำหรับ Core Data, dismiss สำหรับปิดหน้าจอ, และอื่นๆ อีกมากมาย View ที่ประกาศ @Environment ด้วยคีย์เฉพาะจะได้รับค่าปัจจุบันโดยอัตโนมัติ และจะถูกเรียกโหมาระบายใหม่เมื่อมันเปลี่ยนแปลง
สถาปัตยกรรมสภาพแวดล้อม SwiftUI อาศยโปรโตคอล EnvironmentValues — โครงสร้างที่ประกอบด้วยค่าระบบทั้งหมด แต่ละค่าจะถูกเก็บไว้เป็นคุณสมบัติของโครงสร้างนี้ที่มี getter และ setter @Environment ใช้ key path เพื่อเข้าถึงคุณสมบัติเฉพาะ: @Environment(\.colorScheme) — เข้าถึงโครงสี, @Environment(\.locale) — เข้าถึงตำแหน่งที่อยู่
Property wrapper @Environment ใช้กลไกสำคัญสองอย่าง: การอ่านค่าจากสภาพแวดล้อม และการสมัครสมาชิกกับการเปลี่ยนแปลงของมัน เมื่อมีการสร้าง View, SwiftUI จะผ่านคุณสมบัติ @Environment ทั้งหมด และเชื่อมโยงมันกับค่าที่สอดคล้องกับบริบท์ปัจจุบัน หาก View แม่เปลี่ยนแปลงค่าโดยใช้ตัวแก้ไข .environment() View ลูกทั้งหมดที่อ่านค่านั้นจะถูกเรียกโหมาระบายใหม่โดยอัตโนมัติ
คุณสมบัติที่สำคัญ: @Environment รองรับค่าตัวเลือกได้ หากไม่มีการตั้งค่าในลำดับชั้น ระบบจะส่งค่าเริ่มต้นที่กำหนดไว้ใน EnvironmentKey กลับคืน สำหรับคีย์ระบบ ค่าเริ่มต้นจะมีความสมเหตุสมผลเสมอ — ตัวอย่างเช่น, โครงสีเริ่มต้นคือ .light สำหรับ คีย์ที่กำหนดเอง นักพัฒนาจะเป็นผู้กำหนดค่าเริ่มต้นในเมธอด defaultValue ของโปรโตคอล EnvironmentKey
struct EnvironmentReaderView: View {
@Environment(\.colorScheme) var colorScheme
@Environment(\.locale) var locale
@Environment(\.sizeCategory) var sizeCategory
var body: some View {
VStack {
Text("Current scheme: \(colorScheme == .dark ? "Dark" : "Light")")
Text("Locale: \(locale.identifier)")
Text("Font size: \(sizeCategory)")
}
}
}
ในตัวอย่าง View อ่านค่าสภาพแวดล้อมของระบบสามค่า เมื่อ colorScheme เปลี่ยนแปลง — ตัวอย่างเช่น, ผู้ใช้เปิดใช้โหมดมืดในการตั้งค่า — View จะถูกเรียกโหมาระบายใหม่โดยอัตโนมัติด้วยค่าใหม่ เช่นเดียวกันเมื่อภูมิภาคหรือขนาดแบบอักษร (Dynamic Type) เปลี่ยนแปลง View ไม่จำเป็นต้องสมัครสมาชิกกับการแจ้งเตือนหรือเรียก refresh — SwiftUI จัดการเรื่องนี้โดยอัตโนมัติ
SwiftUI มีค่าสภาพแวดล้อมของระบบมากมายที่ครอบคลุมด้านต่างๆ ของส่วนติดต่อผู้ใช้และพฤติกรรม โครงสี (\.colorScheme) เป็นหนึ่งในค่าที่ใช้กันบ่อยที่สุด, ช่วยให้ส่วนติดต่อผู้ใช้ปรับตัวตามโครงสีสว่างและมืด ตำแหน่งที่อยู่ (\.locale) ประกอบด้วยการตั้งค่าภูมิภาคของผู้ใช้สำหรับการจัดรูปแบบวันที่, ตัวเลข และสกุลเงิน
สำหรับ Core Data, ใช้ managedObjectContext (\.managedObjectContext) — บริบท์ที่ส่งต่อผ่านสภาพแวดล้อมจากคอนเทนเนอร์การจัดเก็บข้อมูล สำหรับการนำทาง, มี dismiss (\.dismiss) สำหรับปิดหน้าจอปัจจุบัน และ isPresented (\.isPresented) สำหรับการนำเสนอแบบ modal สำหรัปปฏิทินและโซนเวลา — calendar และ timeZone ตามลำดับ
| Key Path | ชนิด | วัตถุประสงค์ |
|---|---|---|
| \.colorScheme | ColorScheme | โครงสีสว่างหรือมืด |
| \.locale | Locale | การตั้งค่าภูมิภาค |
| \.sizeCategory | ContentSizeCategory | ขนาดแบบอักษร Dynamic Type |
| \.managedObjectContext | NSManagedObjectContext | บริบท์ Core Data |
| \.dismiss | DismissAction | ปิดหน้าจอ |
| \.calendar | Calendar | ปฏิทินปัจจุบัน |
| \.timeZone | TimeZone | โซนเวลา |
| \.horizontalSizeClass | UserInterfaceSizeClass | ขนาดหน้าจอแนวนอน |
เพื่อเข้าถึงค่าระบบ, ใช้ key path ที่มีจุด: @Environment(\.dismiss) var dismiss ตัวคอมไพเลอร์จะตรวจสอบการมีอยู่ของ key path ใน EnvironmentValues, ดังนั้นคีย์ที่ไม่ถูกต้องจะทำให้เกิดข้อผิดพลาดขณะคอมไพล์ Apple เพิ่มค่าระบบใหม่ๆ ในทุกรุ่น iOS — รายชื่อที่ครบถ้วนมีอยู่ในเอกสาร EnvironmentValues
แม้ว่าจะมีชื่อคล้ายกัน แต่ @Environment และ @EnvironmentObject ทำงานคนละวัตถุประสงค์ที่แตกต่างกัน @Environment อ่านค่าระบบหรือค่าที่กำหนดเองที่ลงทะเบียนผ่าน EnvironmentKey @EnvironmentObject เป็น property wrapper สำหรับ ObservableObject ที่ส่งต่อผ่านสภาพแวดล้อมตามชนิด, โดยไม่มีคีย์ที่ชัดเจน
@EnvironmentObject ใช้สำหรับ dependency injection: View แม่สร้างอ็อบเจ็กต์ (เช่น ViewModel) และส่งต่อไปยัง View ลูกโดยใช้ตัวแก้ไข .environmentObject() View ลูกจะได้รับมันผ่าน @EnvironmentObject และสามารถอ่านและแก้ไขคุณสมบัติของมัน ในทางกลับกัน, @Environment เป็นแบบ อ่านอย่างเดียว สำหรับค่าระบบ และไม่รองรับการติชมกลับ
| พารามิเตอร์ | @Environment | @EnvironmentObject |
|---|---|---|
| วัตถุประสงค์ | ค่าระบบและค่าที่กำหนดเอง | การฉีด ObservableObject |
| คีย์ | Key path ของ EnvironmentValues | ตามชนิดของอ็อบเจ็กต์ |
| การเขียน | อ่านอย่างเดียว | อ่านและเขียน |
| ค่าที่กำหนดเอง | ผ่าน EnvironmentKey | ผ่านคลาส ObservableObject |
| ค่าเริ่มต้น | มี (defaultValue) | ไม่มี (ต้องส่งต่อ) |
ในทางปฏิบัติ: ใช้ @Environment สำหรับเข้าถึงพารามิเตอร์ระบบ (โครงสี, ตำแหน่งที่อยู่, ขนาดแบบอักษร) และการกำหนดค่าที่กำหนดเองที่ไม่เปลี่ยนแปลงในระะเวลาทำงาน ใช้ @EnvironmentObject สำหรับการส่งต่อ ViewModel หรือบริการผ่านลำดับชั้น View เมื่อจำเป็นต้องแก้ไขสถานะจากคอมโพนอนต์ย่อย
มาดูการสร้างค่าสภาพแวดล้อมที่กำหนดเอง สำหรับเรื่องนี้, คุณต้องกำหนดโครงสร้างที่ตรงตามโปรโตคอล EnvironmentKey และขยาย EnvironmentValues ด้วยคุณสมบัติใหม่ วิธีนี้ทำให้สามารถส่งการกำหนดค่าโครงสีหรือการตั้งค่าแอปผ่านต้นไม้ View ทั้งหมดโดยไม่ต้องใช้ props
struct AppThemeKey: EnvironmentKey {
static let defaultValue: AppTheme = .system
}
extension EnvironmentValues {
var appTheme: AppTheme {
get { self[AppThemeKey.self] }
set { self[AppThemeKey.self] = newValue }
}
}
enum AppTheme { case system, light, dark }
โปรโตคอล EnvironmentKey ต้องการให้ใช้คุณสมบัติสถิต defaultValue — ค่าที่จะใช้หาก View แม่ไม่ได้ตั้งค่าสภาพแวดล้อมที่กำหนดเอง การขยาย EnvironmentValues จะเพิ่มคุณสมบัติแบบคำนวณ appTheme โดยใช้ subscript กับคีย์ หลังจากนั้น, View ใดๆ ก็สามารถอ่านค่าผ่าน @Environment(\.appTheme)
struct ThemedView: View {
@Environment(\.appTheme) var appTheme
@Environment(\.colorScheme) var colorScheme
var body: some View {
VStack {
if appTheme == .dark || (appTheme == .system && colorScheme == .dark) {
Text("Dark mode active")
.foregroundStyle(.white)
.background(Color.black)
} else {
Text("Light mode active")
.foregroundStyle(.black)
.background(Color.white)
}
}
}
}
struct ContentView: View {
@State private var selectedTheme = AppTheme.system
var body: some View {
ThemedView()
.environment(\.appTheme, selectedTheme)
}
}
ThemedView อ่านสองสภาพแวดล้อม: appTheme ที่กำหนดเอง และ colorScheme ของระบบ การรวมกันช่วยให้สามารถกำหนดค่าโครงสีได้อย่างยืดหยุ่น: ผู้ใช้สามารถเลือกโครงสีสว่าง, มืด หรือตามระบบ หากเลือกตามระบบ, ค่าจะถูกนำจาก colorScheme ซึ่งเปลี่ยนแปลงโดยอัตโนมัติเมื่อสลับโครงสีในการตั้งค่า iOS View แม่ (ContentView) ตั้งค่า appTheme โดยใช้ตัวแก้ไข .environment()
struct ModalView: View {
@Environment(\.dismiss) var dismiss
@State private var name = ""
var body: some View {
NavigationStack {
Form {
TextField("Your name", text: $name)
Button("Save") { dismiss() }
}
.navigationTitle("Edit Profile")
}
}
}
ตัวอย่างนี้แสดงการใช้งานจริงของ dismiss — อินสแตนซ์ของ DismissAction จากสภาพแวดล้อม การเรียก dismiss() เป็นฟังก์ชันจะปิดหน้าจอ modal หรือกลับ NavigationLink ข้อกำหนดเดียวคือ View ต้องถูกนำเสนอแบบ modal หรืออยู่ภายใน NavigationStack dismiss จะถูกกำหนดโดยอัตโนมัติจากบริบท์: หาก View ถูกเปิดเป็น sheet — sheet จะปิด, หากเป็น popover — popover จะปิด
คำถามที่พบบ่อย
ไม่ได้, @Environment เป็นแบบอ่านอย่างเดียว สำหรับเปลี่ยนค่า, ใช้ @EnvironmentObject กับ ObservableObject หรือ @Binding คุณสมบัติของ EnvironmentKey ที่กำหนดเองสามารถมี setter ในส่วนขยายได้ แต่การเปลี่ยนผ่านมันจะไม่ทริเกอร์การอัพเดต UI — เป็นไปได้ทางเทคนิคแต่ไม่แนะนำ
@Binding สร้างการเชื่อมต่อแบบสองทางกับแหล่งความจริง (State, StateObject, ObservableObject) @Environment เป็นการอ่านทางเดียวจาก บริบท์แบบลำดับชั้น @Binding เหมาะสำหรับการส่งข้อมูลไปยัง View ลูก, @Environment — สำหรับการเข้าถึงการตั้งค่าระบบหรือทั่วโลก
กำหนดโครงสร้างที่ใช้โปรโตคอล EnvironmentKey กับ defaultValue แบบสถิต จากนั้นขยาย EnvironmentValues ด้วยคุณสมบัติที่ใช้ getter/setter ผ่าน subscript[key] หลังการลงทะเบียน, ใช้ @Environment(\.yourKey) สำหรับอ่าน และ .environment(\.yourKey, value) สำหรับการตั้งค่า
SwiftUI มีค่าระบบมากกว่า 50 รายการ: colorScheme, locale, sizeCategory, managedObjectContext, dismiss, calendar, timeZone, horizontalSizeClass, verticalSizeClass, accessibilityEnabled, layoutDirection, legibilityWeight และอื่นๆ รายชื่อที่ครบถ้วนอยู่ในเอกสาร EnvironmentValues
ได้, @Environment ทำงานใน Preview แต่ค่าเริ่มต้นอาจแตกต่างจากเครื่องจำลอง สำหรับทดสอบใน Preview, ใช้ตัวแก้ไข .environment() โดยตรงในโค้ด Preview: ThemedView().environment(\.colorScheme, .dark) วิธีนี้ช่วยให้สามารถตรวจสอบสถานะของสภาพแวดล้อมต่างๆ ได้อย่างเป็นภาพ
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม