Modal Presentation — วิธีการแสดงหน้าจอซ้อนทับบริบทปัจจุบันโดยบล็อกการโต้ตอบกับอินเทอร์เฟซก่อนหน้า ในการพัฒนาโมบายล์ หน้าต่างโมดอลใช้สำหรับงานที่ต้องการโฟกัส: การป้อนข้อมูล การยืนยันการกระทำ การเข้าสู่ระบบ และการเลือกตัวเลือก จากข้อมูลของ Apple HIG, 2025 การนำเสนอแบบโมดอลไม่ควรเกิน 20% ของสถานการณ์การนำทางในแอปพลิเคชัน ใน Android ความเป็นโมดอลถูกใช้งานผ่าน DialogFragment, BottomSheet และ Activity ที่มี launch-แฟล็กบางอย่าง
สาระสำคัญ
Modal Presentation คือแพทเทิร์นการนำทางที่หน้าจอใหม่ปรากฏเหนือหน้าจอปัจจุบัน โดยบล็อกการโต้ตอบกับเนื้อหาของหน้าจอแม่ชั่วคราว ผู้ใช้ต้องสิ้นสุดงานโมดอลอย่างชัดเจน (ยกเลิก บันทึก ปิด) เพื่อกลับสู่สถานะก่อนหน้า
ความเป็นโมดอลช่วยแก้ปัญหาเชิงการรู้คิด: มันทำให้ผู้ใช้โฟกัสที่การกระทำเดียวโดยไม่ถูกรบกวนจากอินเทอร์เฟซที่เหลือ สิ่งนี้สำคัญมากสำหรับฟอร์มลงทะเบียน ไดอะล็อกยืนยัน การเลือกไฟล์ และการเข้าสู่ระบบผ่านบริการบุคคลที่สาม Human Interface Guidelines ของ Apple แนะนำให้ใช้ความเป็นโมดอลเฉพาะกับงานที่ต้องทำให้เสร็จก่อนดำเนินการต่อ
ต่างจากหน้าต่างโมดอลบนเว็บ Modal Presentation บนมือถืออาจเป็นแบบเต็มหน้าจอ (ครอบครองทั้งหน้าจอ) หรือบางส่วน (Page Sheet, Bottom Sheet) การเลือกประเภทขึ้นอยู่กับบริบทของงานและข้อตกลงของแพลตฟอร์ม iOS นิยมใช้ Page Sheet สำหรับสถานการณ์ส่วนใหญ่ และปล่อยให้ Full Screen สำหรับวิดีโอเพลเยอร์และตัวแก้ไขภาพถ่าย
Push Presentation (การนำทางแบบสแตก) เพิ่มหน้าจอเข้าในสแตกการนำทางและแสดงปุ่มย้อนกลับโดยอัตโนมัติ ผู้ใช้สามารถกลับไปยังหน้าจอก่อนหน้าได้ทุกเมื่อ ในทางกลับกัน Modal Presentation ต้องมีการสิ้นสุดที่ชัดเจน: ปุ่มย้อนกลับอาจไม่มี หรือปิดหน้าต่างโมดอลแทนที่จะกลับไปยังหน้าจอก่อนหน้า
ความแตกต่างหลักระหว่างการนำเสนอแบบโมดอลและแบบ push: Modal Presentation บล็อกการนำทางย้อนกลับโดยไม่สูญเสียข้อมูล ต้องมีการกระทำเพื่อปิด (Save, Cancel, Done) และโดยปกติแสดงถึงงานที่แยกต่างหาก Push Presentation รักษาลำดับชั้นการนำทาง เพิ่มปุ่มย้อนกลับโดยอัตโนมัติ และเหมาะสำหรับการดูเนื้อหาแบบเรียงลำดับ
| คุณลักษณะ | Modal Presentation | Push Presentation |
|---|---|---|
| การบล็อกการย้อนกลับ | ใช่ ต้องมีการกระทำที่ชัดเจน | ไม่ ปุ่มย้อนกลับพร้อมใช้เสมอ |
| การใช้งานทั่วไป | ฟอร์ม การเข้าสู่ระบบ การเลือก | การดูรายละเอียด การนำทาง |
| แอนิเมชัน | จากล่างขึ้นบน (iOS), สไลด์ (Android) | จากขวาไปซ้าย (iOS) |
| สแตกการนำทาง | ไม่ถูกเพิ่มในสแตกหลัก | ถูกเพิ่มในสแตก |
ในทางปฏิบัติ การเลือกระหว่าง Modal และ Push ขึ้นอยู่กับบริบท แนะนำให้ใช้ความเป็นโมดอลสำหรับงานที่ผู้ใช้ต้องทำให้เสร็จก่อนดำเนินการต่อ และใช้ Push สำหรับการศึกษาเนื้อหาแบบเรียงลำดับ การผสมแพทเทิร์นบนหน้าจอเดียวทำให้เกิดความสับสนและทำให้ UX แย่ลง
iOS มีรูปแบบการนำเสนอแบบโมดอลหลายแบบผ่าน enum UIModalPresentationStyle UIKit รองรับ .fullScreen (เต็มหน้าจอ), .pageSheet (การ์ดที่มีระยะห่างด้านบน), .formSheet (หน้าต่างตรงกลางบน iPad) และ .automatic (ระบบเลือกตามบริบท) ตั้งแต่ iOS 13 รูปแบบเริ่มต้นคือ .automatic ซึ่งสำหรับ iPhone เลือก .pageSheet
เมธอดพื้นฐานของ UIKit สำหรับการนำเสนอแบบโมดอลคือ present(_:animated:completion:) คอนโทรลเลอร์ที่เรียกเมธอดกลายเป็น presentingViewController และตัวใหม่เป็น presentedViewController การปิดทำผ่าน dismiss(animated:completion:) SwiftUI มีตัวปรับแต่ง .sheet สำหรับพฤติกรรมที่คล้ายกัน
แนวทางแบบประกาศของ SwiftUI ใช้ตัวปรับแต่ง .sheet และ .fullScreenCover ตัวแรกสร้าง Page Sheet ตัวที่สองสร้างการนำเสนอแบบโมดอลแบบเต็มหน้าจอ ทั้งคู่รับ binding บน Bool หรือออบเจกต์ที่ระบุตัวตนได้ ซึ่งควบคุมการแสดงผลของหน้าต่างโมดอล การปิดเกิดขึ้นเมื่อตั้งค่า binding เป็น false หรือเรียก dismiss จาก environment
struct ContentView: View {
@State private var showModal = false
var body: some View {
Button("เปิดฟอร์ม") {
showModal = true
}
.sheet(isPresented: $showModal) {
RegistrationForm()
}
}
}
struct RegistrationForm: View {
@Environment(\.dismiss) private var dismiss
var body: some View {
Button("บันทึก") { dismiss() }
}
}Android ไม่มี API เดียวสำหรับการนำเสนอแบบโมดอลเหมือน iOS แต่แพลตฟอร์มมีกลไกหลายอย่าง: DialogFragment สำหรับหน้าต่างไดอะล็อก BottomSheetDialogFragment สำหรับแผงด้านล่าง และ Activity ที่มีแฟล็ก NEW_TASK และ CLEAR_TOP สำหรับหน้าจอโมดอล ใน Jetpack Compose มีคอมโพเนนต์ Dialog เดียวสำหรับหน้าต่างโมดอลทุกประเภท
DialogFragment เป็นคลาสพื้นฐานสำหรับหน้าต่างโมดอลใน Android SDK มันจัดการวงจรชีวิตของไดอะล็อก จัดการการหมุนหน้าจอ และบันทึกสถานะ ฟรากเมนต์แสดงเหนือ Activity โดยไม่บล็อกสแตกการนำทาง การปิดทำผ่าน dismiss() หรือการกดนอกพื้นที่ไดอะล็อกหาก setCancelable(true)
BottomSheetDialogFragment แสดงหน้าต่างโมดอลเป็นแผงที่เลื่อนขึ้นจากด้านล่าง แพทเทิร์นนี้เป็นที่นิยมใน Material Design สำหรับการเลือกตัวเลือก การแชร์ และการกระทำด่วน BottomSheet อาจมีความสูงคงที่หรือขยายได้ (peek height + full height) ใน Compose ใช้ ModalBottomSheet จากไลบรารี Material3
@Composable
fun ModalScreen(onDismiss: () -> Unit) {
Dialog(onDismissRequest = onDismiss) {
Card(
modifier = Modifier.padding(16.dp)
) {
Column {
Text("ฟอร์มโมดอล", style = MaterialTheme.typography.headlineSmall)
Button(onClick = onDismiss) {
Text("ปิด")
}
}
}
}
}หน้าต่างโมดอล เป็นเครื่องมือ UX ที่ทรงพลัง แต่การใช้งานมากเกินไปทำให้ประสบการณ์ผู้ใช้แย่ลง Apple HIG และ Google Material Design เห็นพ้องในคำแนะนำ: ความเป็นโมดอลควรใช้สำหรับงานที่ต้องโฟกัสและไม่เกิน 20% ของการกระทำการนำทางทั้งหมด
หน้าต่างโมดอลเหมาะสำหรับสถานการณ์: การป้อนข้อมูล (ฟอร์มลงทะเบียน โปรไฟล์) การยืนยัน (การลบ การส่ง) การเลือก (ตัวเลือกวันที่ ตัวจัดการไฟล์) และ การเข้าสู่ระบบ (OAuth, Firebase Auth) หากงานใช้เวลาน้อยกว่า 30 วินาทีและต้องบล็อกบริบท — เลือกความเป็นโมดอล
อย่าใช้การนำเสนอแบบโมดอลสำหรับ: การดูเนื้อหาแบบเรียงลำดับ (ใช้ Push) การแสดงข้อผิดพลาด (ใช้ Toast หรือ Snackbar) โฆษณาและข้อเสนอส่งเสริมการขายโดยไม่มีการร้องขอจากผู้ใช้อย่างชัดเจน Material Design แนะนำให้หลีกเลี่ยงหน้าต่างโมดอลแบบซ้อนกัน — มันทำให้ผู้ใช้สับสนและทำลายลำดับชั้นการนำทาง
สำหรับหน้าต่างโมดอลที่มีช่องข้อความ ต้องจัดการการสูญเสียโฟกัสของคีย์บอร์ดอย่างแน่นอน เมื่อคีย์บอร์ดปรากฏ หน้าต่างโมดอลควรเลื่อนขึ้นเพื่อให้ผู้ใช้เห็นข้อความที่ป้อน UIKeyboardWillShowNotification ใน iOS และ adjustResize ใน Android แก้ปัญหานี้
มาดูการนำเสนอแบบโมดอลบนทั้งสองแพลตฟอร์ม Swift ตัวอย่างแสดงการตั้งค่า UIModalPresentationStyle.pageSheet พร้อม delegate สำหรับจัดการการปิด Kotlin ตัวอย่างแสดง DialogFragment ที่มี layout แบบกำหนดเองและการบันทึกสถานะ
let modalVC = ModalViewController()
modalVC.modalPresentationStyle = .pageSheet
if let sheet = modalVC.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.prefersGrabberVisible = true
}
modalVC.presentationController?.delegate = self
present(modalVC, animated: true)
// MARK: - UIAdaptivePresentationControllerDelegate
extension ViewController: UIAdaptivePresentationControllerDelegate {
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
print("ปิดโมดอลแล้ว")
}
}class ModalDialogFragment : DialogFragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_modal, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
view.findViewById<Button>(R.id.closeButton).setOnClickListener {
dismiss()
}
}
}คำถามที่พบบ่อย
ใช้ Modal Presentation สำหรับงานที่ต้องโฟกัสซึ่งผู้ใช้ต้องทำให้เสร็จก่อนดำเนินการต่อ: ฟอร์ม การยืนยัน การเข้าสู่ระบบ Push เหมาะสำหรับการดูเนื้อหาแบบเรียงลำดับที่ผู้ใช้สามารถกลับไปมาได้อย่างอิสระ หน้าต่างโมดอลไม่ควรมีการนำทางภายในตัวเอง
ตั้งแต่ iOS 13 รูปแบบเริ่มต้น .automatic เลือก .pageSheet สำหรับ iPhone .pageSheet เหมาะกับสถานการณ์ส่วนใหญ่ (ฟอร์ม รายละเอียด) .fullScreen — สำหรับเนื้อหาสื่อ (วิดีโอ ตัวแก้ไขภาพถ่าย) .formSheet — สำหรับแอป iPad ที่ต้องการหน้าต่างตรงกลาง
Jetpack Compose มีคอมโพเนนต์ Dialog สำหรับหน้าต่างโมดอลแบบง่าย และ ModalBottomSheet สำหรับแผงด้านล่าง Dialog รับ onDismissRequest และเนื้อหาแบบ Compose สำหรับการบันทึกสถานะ ให้ใช้ rememberSaveable ภายในไดอะล็อก
Apple HIG และ Material Design ไม่แนะนำให้ใช้หน้าต่างโมดอลแบบซ้อนกัน หากผู้ใช้เปิดหน้าต่างโมดอลทับหน้าต่างโมดอลอื่น พวกเขาจะสูญเสียบริบทและอาจสับสนในลำดับชั้น แทนการซ้อนกัน ให้ใช้ Step Indicator หรือแพทเทิร์น Wizard กับหน้าต่างโมดอลเดียว
ใช้ UIAdaptivePresentationControllerDelegate ใน iOS (เมธอด presentationControllerShouldDismiss) หรือ OnBackPressedDispatcher ใน Android เมื่อมีข้อมูลที่ยังไม่ได้บันทึก ให้แสดง AlertDialog พร้อมตัวเลือก: บันทึก ยกเลิกการเปลี่ยนแปลง อยู่บนหน้าจอต่อ สิ่งนี้ป้องกันการสูญเสียข้อมูลโดยไม่ตั้งใจของผู้ใช้
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม