NavigationStack คือคอนเทนเนอร์การนำทางที่ทันสมัยใน SwiftUI ซึ่งเปิดตัวใน iOS 16+ และแทนที่ NavigationView ตาม เอกสารสำหรับนักพัฒนา Apple, 2024 NavigationStack จัดการสแต็กของหน้าจอผ่านเส้นทางการนำทางที่ปลอดภัยชนิด (NavigationPath) รองรับการนำทางเชิงลึก การกลับไปยังหน้าจอหลักโดยทางโปรแกรม และการรักษาสถานะเมื่อข้อมูลเปลี่ยนแปลง แตกต่างจาก NavigationView ตรงที่ NavigationStack ไม่ต้องการการห่อในคอนเทนเนอร์เพิ่มเติมและให้ Binding โดยตรงกับเส้นทางการนำทาง
ประเด็นสำคัญ
NavigationStack คือ View คอนเทนเนอร์ที่ใช้การนำทางแบบสแต็ก (LIFO) มันจัดการประวัติการเปลี่ยนหน้าจอ ทำให้คุณสามารถวางหน้าจอใหม่ลงบนสแต็กและกลับไปข้างหน้าผ่านปุ่ม “ย้อนกลับ” ของระบบหรือโดยทางโปรแกรม NavigationStack เป็นส่วนหนึ่งของ SwiftUI ตั้งแต่ iOS 16, iPadOS 16, macOS 13, watchOS 9 และ tvOS 16
นวัตกรรมหลักของ NavigationStack คือเส้นทางการนำทางที่ปลอดภัยชนิด แทนที่จะระบุปลายทางโดยตรงเมื่อสร้าง NavigationLink คุณใส่ค่าลงในเส้นทาง และ View ปลายทางจะถูกลงทะเบียนแยกต่างหากผ่านตัวปรับแต่ง .navigationDestination(for:destination:) สิ่งนี้แยกการนำทางออกจากการแสดงผล ทำให้โค้ดเป็นโมดูลาร์และทดสอบได้มากขึ้น
ตาม WWDC 2022 (เซสชัน 10054) NavigationStack ใช้กลไกการนำทางใหม่ที่อิงตาม ObservableObject และวงจรชีวิตของ SwiftUI แตกต่างจาก NavigationView ที่พึ่งพา UINavigationController ภายใน NavigationStack ถูกนำไปใช้ใน SwiftUI อย่างสมบูรณ์ ซึ่งช่วยปรับปรุงความสามารถในการคาดเดาและความเข้ากันได้กับวงจรชีวิตของ SwiftUI
NavigationStack รับ View หลักและเส้นทางการนำทางที่เป็นตัวเลือก (Binding ไปยัง NavigationPath หรืออาร์เรย์ของค่า Hashable) หน้าจอย่อยทั้งหมดจะถูกวางลงบนสแต็กผ่าน NavigationLink หรือโดยการเพิ่มค่าโดยทางโปรแกรมลงในเส้นทาง
NavigationView เป็นคอนเทนเนอร์การนำทางหลักใน SwiftUI ก่อน iOS 16 มันจัดการ UINavigationController โดยอัตโนมัติภายใน ซึ่งนำไปสู่ปัญหาหลายประการ: พฤติกรรมที่คาดเดาไม่ได้เมื่อข้อมูลเปลี่ยนแปลง ความซับซ้อนกับการนำทางโดยทางโปรแกรม และการขาดความปลอดภัยชนิด
| คุณลักษณะ | NavigationStack (iOS 16+) | NavigationView (iOS 13–15) |
|---|---|---|
| ชนิดการนำทาง | สแต็ก (LIFO) | สแต็ก (LIFO) |
| เส้นทางการนำทาง | กำหนดชนิด (NavigationPath) | ไม่รองรับ |
| Deep linking | รองรับในตัว | ต้องใช้วิธีแก้ |
| การกลับโดยทางโปรแกรม | ผ่านเส้นทาง (pop, popToRoot) | dismiss, presentationMode |
| ภายใน | SwiftUI ดั้งเดิม | UINavigationController |
| ความเข้ากันได้ | iOS 16+ | iOS 13+ |
ข้อได้เปรียบหลัก ของ NavigationStack — การนำทางที่ปลอดภัยชนิด คุณกำหนดเส้นทางเป็นอาร์เรย์ของชนิดเฉพาะ (หรือ NavigationPath สำหรับสแต็กแบบต่างชนิด) และลงทะเบียนปลายทางสำหรับแต่ละชนิด สิ่งนี้กำจัดข้อผิดพลาดชนิดไม่ตรงกันและทำให้การนำทางคาดเดาได้
NavigationView เลิกใช้งานแล้วใน iOS 17 Apple แนะนำให้ย้ายไปใช้ NavigationStack สำหรับโปรเจ็กต์ใหม่ทั้งหมดและเมื่ออัปเดตเวอร์ชันขั้นต่ำเป็น iOS 16
NavigationPath คือชนิดที่แสดงถึงเส้นทางการนำทางใน NavigationStack มันสามารถเก็บค่าต่างชนิด (AnyHashable) หรือใช้กับชนิดเฉพาะผ่าน Binding ไปยังอาร์เรย์ [T: Hashable] NavigationPath เข้ารหัสและถอดรหัสโดยอัตโนมัติเพื่อรักษาสถานะ
struct ContentView: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: String.self) { value in
DetailView(id: value)
}
.navigationDestination(for: Int.self) { value in
NumberView(number: value)
}
}
}
func goToRoot() {
path.removeLast(path.count)
}
func pushDeepLink() {
path.append("detail_42")
}
}
สแต็กต่างชนิด: NavigationPath สามารถมีค่าของชนิดต่าง ๆ ได้หากใช้ Hashable ตัวอย่างเช่น หน้าจอแรกอาจรับ String (ID) หน้าจอที่สองรับ Int (ตัวเลข) หน้าจอที่สามรับ enum Route ที่กำหนดเอง แต่ละชนิดลงทะเบียน .navigationDestination แยกต่างหากสำหรับการแสดงผล
การรองรับ Codable: NavigationPath ใช้ Codable หากค่าทั้งหมดในเส้นทางเป็น Codable + Hashable ด้วย ซึ่งช่วยให้บันทึกและกู้คืนสถานะการนำทางเมื่อเริ่มต้นแอปใหม่หรือเมื่อเข้าสู่พื้นหลัง
.navigationDestination(for:destination:) — ตัวปรับแต่งที่ลงทะเบียน View ปลายทางสำหรับชนิดข้อมูลเฉพาะ เมื่อ NavigationLink วางค่าของชนิดนี้ลงในเส้นทาง SwiftUI จะค้นหา .navigationDestination ที่เกี่ยวข้องโดยอัตโนมัติและสร้างหน้าจอ
enum AppRoute: Hashable {
case profile(UserID)
case settings
case about
}
struct AppNavigation: View {
@State private var path = NavigationPath()
var body: some View {
NavigationStack(path: $path) {
HomeView()
.navigationDestination(for: AppRoute.self) { route in
switch route {
case .profile(let id):
ProfileView(userId: id)
case .settings:
SettingsView()
case .about:
AboutView()
}
}
}
}
}
// Navigate: path.append(AppRoute.profile("user_123"))
กฎสำคัญ: .navigationDestination ต้องใช้กับ View ที่อยู่ภายใน NavigationStack และก่อนที่ NavigationLink จะวางค่าลงในเส้นทาง โดยปกติจะเพิ่มไปยัง View หลักหรือคอนเทนเนอร์ส่วน หากไม่พบ .navigationDestination สำหรับชนิดของค่า การเปลี่ยนหน้าจะไม่เกิดขึ้น
ตาม SwiftUI Engineering (2023) .navigationDestination สามารถลงทะเบียนได้ในระดับต่าง ๆ ของลำดับชั้น SwiftUI ค้นหาปลายทางที่ตรงกันใกล้ที่สุดระหว่างการนำทาง ซึ่งช่วยให้สามารถแทนที่ปลายทางสำหรับชนิดหนึ่งในส่วนต่าง ๆ ของแอปพลิเคชัน
รูปแบบ 1: การนำทางผ่าน enum Route กำหนด enum ด้วยค่าที่เกี่ยวข้องสำหรับหน้าจอทั้งหมดในแอปพลิเคชัน ใช้ .navigationDestination หนึ่งตัวสำหรับ AppRoute และ switch สำหรับการกำหนดเส้นทาง สิ่งนี้ให้แหล่งความจริงเดียวสำหรับการเปลี่ยนหน้าทั้งหมดที่เป็นไปได้ในแอปพลิเคชัน
struct StoreView: View {
@State private var path: [ProductRoute] = []
var body: some View {
NavigationStack(path: $path) {
ProductGrid()
.navigationDestination(for: ProductRoute.self) { route in
switch route {
case .detail(let product):
ProductDetail(product: product)
case .reviews(let productId):
ReviewsView(productId: productId)
}
}
}
}
}
enum ProductRoute: Hashable {
case detail(Product)
case reviews(String)
}
รูปแบบ 2: การนำทางโดยทางโปรแกรมและ deep linking NavigationStack อนุญาตให้จัดการสแต็กโดยทางโปรแกรม: เพิ่ม ลบหน้าจอ และกลับไปยังหน้าจอหลัก สิ่งนี้จำเป็นสำหรับการแจ้งเตือนแบบพุช ดีพลิงก์ และการกู้คืนการนำทางหลังจากเริ่มต้นใหม่
รูปแบบ 3: อาร์เรย์ของชนิดแทน NavigationPath หากหน้าจอทั้งหมดใช้ชนิดเดียวกัน (เช่น String หรือ enum ที่กำหนดเอง) ให้ใช้ Binding ไปยัง [T] สิ่งนี้ให้ชนิดที่แข็งแกร่งกว่าและประสิทธิภาพที่ดีกว่า NavigationPath NavigationPath เหมาะสมสำหรับสแต็กต่างชนิดที่มีชนิดหน้าจอแตกต่างกัน
ตาม Point-Free (2024) NavigationStack กับ enum Route เป็นวิธีที่ต้องการในการจัดระเบียบการนำทางในแอปพลิเคชัน SwiftUI มันทำให้การเปลี่ยนหน้าทั้งหมดที่เป็นไปได้ชัดเจน ปลอดภัยชนิด และทดสอบได้ ซึ่งสำคัญโดยเฉพาะสำหรับโปรเจ็กต์ขนาดใหญ่ที่มีหน้าจอหลายสิบหน้า
คำถามที่พบบ่อย
NavigationStack — คอนเทนเนอร์การนำทาง SwiftUI (iOS 16+) ที่จัดการสแต็กของหน้าจอผ่านเส้นทางที่ปลอดภัยชนิด มันแทนที่ NavigationView โดยรองรับ deep linking การนำทางโดยทางโปรแกรม และการรักษาสถานะ
NavigationStack ใช้ เส้นทางที่ปลอดภัยชนิด (NavigationPath) แทนการผูก NavigationLink โดยตรงกับปลายทาง รองรับการนำทางโดยทางโปรแกรม deep linking และ Codable สำหรับการรักษาสถานะ มันทำงานบน SwiftUI ไม่ผ่าน UINavigationController
NavigationPath คือชนิดที่แสดงลำดับของหน้าจอในสแต็ก คุณเพิ่มค่าลงในเส้นทางผ่าน path.append() หรือ NavigationLink พร้อมค่า แต่ละชนิดลงทะเบียน .navigationDestination สำหรับการแสดงผล NavigationPath รองรับ Codable และการรักษาสถานะอัตโนมัติ
ผ่าน การจัดการเส้นทางโดยทางโปรแกรม: หลังจากประมวลผล URL ให้เรียก path.append() ด้วยค่าเส้นทางที่เกี่ยวข้อง NavigationStack จะแสดงหน้าจอเป้าหมายโดยอัตโนมัติ กลับไปยังหน้าจอหลัก — path.removeLast(path.count)
ใช่ หากเวอร์ชันขั้นต่ำของคุณคือ iOS 16+ NavigationView เลิกใช้งานแล้วใน iOS 17 การย้ายให้ การนำทางที่ปลอดภัยชนิด การรองรับ deep linking และการสอดคล้องกับวงจรชีวิต SwiftUI ที่ดีกว่า สำหรับโปรเจ็กต์ที่กำหนดเป้าหมาย iOS 15 และต่ำกว่า ให้ใช้ NavigationView ต่อไปก่อน
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ