Sheet เป็นส่วนประกอบ UI ที่ปรากฏที่ด้านล่างของหน้าจอเหนือเนื้อหาหลักและให้ข้อมูลหรือการกระทำเพิ่มเติม Bottom Sheet เป็นชนิดที่พบได้บ่อยที่สุด ซึ่งกำหนดไว้ในแนวทางของ Material Design 3 (Google, 2024) แตกต่างจากหน้าต่างโต้ตอบตรงที่ Sheet ไม่ได้บล็อกการโต้ตอบกับพื้นหลังอย่างสมบูรณ์และรองรับสถานะ collapsed และ expanded การทำความเข้าใจ ชนิดและพฤติกรรมของ Sheet เป็นสิ่งจำเป็นสำหรับการสร้างการนำทางที่สะดวกในแอปพลิเคชันมือถือ
ประเด็นสำคัญ
Sheet เป็นส่วนประกอบ UI ระดับพื้นผิวที่เลื่อนขึ้นจากด้านล่างของหน้าจอและแสดงเนื้อหาหรือการกระทำเพิ่มเติมโดยไม่พาผู้ใช้ออกจากหน้าจอปัจจุบัน แตกต่างจากการเปลี่ยนหน้าแบบเต็มจอ ตรงที่ Sheet จะรักษาบริบทไว้: ผู้ใช้สามารถเห็นหน้าจอพื้นหลังและเข้าใจว่า Sheet มาจากไหน
สถาปัตยกรรมของ Sheet ขึ้นอยู่กับ คอนเทนเนอร์ที่ปรับความสูงได้ ในสถานะขั้นต่ำ (collapsed) จะมองเห็นเพียงส่วนหนึ่งของเนื้อหา — โดยปกติคือชื่อหรือแผงขนาดเล็ก ผู้ใช้สามารถลาก Sheet ขึ้นเพื่อขยาย (expanded) หรือปัดลงเพื่อปิด พฤติกรรมนี้คล้ายกับแผ่นกระดาษจริงที่ถูกดึงออกมาจากใต้กอง — จึงเป็นที่มาของชื่อ
Google ในแนวทาง Material Design 3 (2024) เน้น สองสถานการณ์หลัก สำหรับการใช้ Sheet: การแสดงการกระทำเพิ่มเติมที่ไม่พอดีกับแถบเครื่องมือหลัก และการแสดงฟอร์มหรือข้อมูลโดยละเอียดโดยไม่ต้องนำทางไปยังหน้าจอแยกต่างหาก บน iOS Apple ได้เพิ่มการรองรับ Sheet ระดับระบบตั้งแต่ iOS 15 ผ่าน UISheetPresentationController ซึ่งยืนยันความเป็นสากลของรูปแบบนี้
Material Design กำหนด Bottom Sheet สามชนิด แต่ละชนิดมีพฤติกรรมและขอบเขตการใช้งานของตัวเอง การเลือกชนิดที่ถูกต้องส่งผลโดยตรงต่อประสบการณ์ผู้ใช้และการปฏิบัติตามแนวทางของแพลตฟอร์ม
Bottom Sheet มาตรฐาน เป็นส่วนประกอบที่ไม่ใช่ modal: ผู้ใช้สามารถโต้ตอบกับหน้าจอพื้นหลังได้โดยไม่ต้องปิด Sheet ตัวอย่างเช่น ขณะเรียกดูรายการสินค้าในพื้นหลัง ผู้ใช้จะเห็นตัวกรองใน Bottom Sheet และสามารถเปลี่ยนแปลงได้โดยไม่ต้องปิดแผง Sheet ปรับความสูงตามเนื้อหา — ตั้งแต่ 30% ถึง 90% ของหน้าจอ
Bottom Sheet มาตรฐาน ไม่บล็อกพื้นหลัง และอนุญาตให้โต้ตอบกับเนื้อหาหลัก นี่คือความแตกต่างหลักจาก Sheet แบบ modal ซึ่งต้องการการกระทำที่จำเป็นเพื่อดำเนินการต่อ ใช้ Sheet มาตรฐานเมื่อข้อมูลเพิ่มเติมไม่สำคัญต่อสถานการณ์หลัก
Bottom Sheet แบบ Modal บล็อกการโต้ตอบกับหน้าจอพื้นหลังจนกว่าผู้ใช้จะดำเนินการภายใน Sheet Sheet ชนิดนี้จะแสดงในสถานะ expanded เสมอและต้องการการปิดอย่างชัดเจน — ผ่านปุ่ม การกระทำ หรือการปัดลง Material Design แนะนำ Sheet แบบ modal สำหรับฟอร์มเข้าสู่ระบบ การยืนยัน และการตั้งค่าที่สำคัญ
บน Android Bottom Sheet แบบ modal ถูกนำไปใช้ผ่าน BottomSheetDialogFragment ซึ่งบล็อกพื้นหลังโดยอัตโนมัติ บน iOS พฤติกรรมที่คล้ายกันนั้นมาจากตัวแปร modal ของ UISheetPresentationController ด้วยพารามิเตอร์ .mediumDetent ควรใช้ Sheet แบบ modal เฉพาะเมื่อบริบทต้องการความสนใจจากผู้ใช้
Bottom Sheet แบบขยาย รวมคุณสมบัติของแบบมาตรฐานและ modal: เริ่มต้นเป็นแบบไม่ใช่ modal ในสถานะ collapsed แต่เมื่อลากขึ้นจะกลายเป็น modal ในทางปฏิบัติหมายความว่าผู้ใช้สามารถดูเนื้อหาคร่าวๆ ก่อน แล้วจึงตัดสินใจ — ขยาย Sheet เพื่อศึกษารายละเอียดหรือปิด
รูปแบบนี้เป็นที่นิยมโดยเฉพาะใน แผนที่และแอปนำทาง ซึ่งแผงด้านล่างแสดงข้อมูลสั้นๆ เกี่ยวกับจุดเส้นทาง และเมื่อปัดขึ้นจะขยายพร้อมรายละเอียด Google Maps ใช้ Sheet แบบขยายนี้เพื่อแสดงข้อมูลเกี่ยวกับสถานที่และเส้นทาง
| ชนิดของ Sheet | การบล็อกพื้นหลัง | สถานะเริ่มต้น | ตัวอย่าง |
|---|---|---|---|
| มาตรฐาน | ไม่ | Collapsed | ตัวกรองในแคตตาล็อก |
| Modal | ใช่ | Expanded | ฟอร์มเข้าสู่ระบบ |
| แบบขยาย | บางส่วน | Collapsed | การ์ดสถานที่ |
Android SDK มี API หลายตัวสำหรับการนำ Bottom Sheet ไปใช้ ตั้งแต่แนวทางแบบ View ดั้งเดิมไปจนถึง Jetpack Compose สมัยใหม่ ไลบรารี Material Components สำหรับ Android มีการนำ BottomSheetDialogFragment และ BottomSheetBehavior มาใช้สำเร็จรูป
BottomSheetBehavior เป็นคลาสจาก Material Components ที่แนบไปกับ View ใดๆ และควบคุมตำแหน่งบนหน้าจอ นักพัฒนากำหนดสามสถานะ: STATE_COLLAPSED, STATE_EXPANDED และ STATE_HIDDEN Behavior จัดการท่าทางการลากและภาพเคลื่อนไหวโดยอัตโนมัติ
val sheet = findViewById<View>(R.id.bottom_sheet)
val behavior = BottomSheetBehavior.from(sheet)
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
behavior.peekHeight = 200
Jetpack Compose มี API ที่ประกาศได้มากขึ้นผ่านคอมโพเนนต์ ModalBottomSheet จาก Material 3 แตกต่างจากแนวทางแบบ View ใน Compose Bottom Sheet เป็นฟังก์ชันคอมโพสเอเบิลที่รับสถานะและเนื้อหา Sheet จะสร้างภาพเคลื่อนไหวโดยอัตโนมัติเมื่อปรากฏ ซ่อน และเปลี่ยนแปลงความสูงเมื่อลาก
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun FilterSheet(onDismiss: () -> Unit) {
ModalBottomSheet(onDismissRequest = onDismiss) {
Text("ตัวกรอง")
Button(onClick = onDismiss) { Text("นำไปใช้") }
}
}
BottomSheetDialogFragment เป็น DialogFragment เฉพาะทางที่แสดงเป็น Bottom Sheet จัดการความเป็น modal วงจรชีวิต และการเปลี่ยนแปลงการกำหนดค่าโดยอัตโนมัติ นักพัฒนาเพียงแค่ต้องแทนที่ onCreateView และคืนค่าเลย์เอาต์ของ Sheet ตามเอกสารของ Google (Android Developers, 2024) BottomSheetDialogFragment เป็นวิธีที่แนะนำสำหรับ Bottom Sheet แบบ modal ใน Android ดั้งเดิม
Apple เริ่มรองรับ Sheet ระดับระบบตั้งแต่ iOS 15 ผ่าน UISheetPresentationController ก่อนหน้านั้น นักพัฒนาต้องนำ Bottom Sheet ไปใช้ด้วยตนเองผ่านการเปลี่ยนหน้าที่กำหนดเองหรือไลบรารีของบุคคลที่สาม Sheet ดั้งเดิมของ Apple รองรับ detents — ระดับการขยายที่กำหนดไว้ล่วงหน้า
UISheetPresentationController เป็นตัวควบคุมการนำเสนอในตัวที่แสดง View Controller เป็น Sheet นักพัฒนากำหนดอาร์เรย์ของ detents — ค่าความสูงที่ Sheet สามารถหยุดได้ iOS 16 เพิ่ม detents แบบกำหนดเองด้วยความสูงที่กำหนดเอง Apple แนะนำให้ใช้ detents แบบ medium (.medium) และ large (.large) เป็นหลัก
let viewController = MySheetViewController()
if let sheet = viewController.sheetPresentationController {
sheet.detents = [.medium, .large]
sheet.prefersGrabberVisible = true
sheet.preferredCornerRadius = 16
}
present(viewController, animated: true)
SwiftUI มีตัวปรับแต่ง .sheet ซึ่งเชื่อมโยงการแสดง Sheet กับสถานะ เมื่อตัวแปร published กลายเป็น true SwiftUI จะแสดง Sheet โดยอัตโนมัติ SwiftUI ยังรองรับ detents ผ่านตัวปรับแต่ง .presentationDetents ซึ่งเพิ่มใน iOS 16
struct ContentView: View {
@State private var showSheet = false
var body: some View {
Button("แสดง Sheet") { showSheet = true }
.sheet(isPresented: $showSheet) {
FilterView()
.presentationDetents([.medium, .large])
}
}
}
เฟรมเวิร์ก ข้ามแพลตฟอร์ม ก็รองรับ Bottom Sheet เช่นกัน โดยปรับพฤติกรรมตามแพลตฟอร์มเป้าหมาย Flutter และ React Native มีการนำไปใช้ในตัวและแบบกำหนดเองด้วยความยืดหยุ่นในระดับต่างๆ
Flutter SDK มีสองเมธอดสำหรับแสดง Bottom Sheet: showBottomSheet (มาตรฐาน) และ showModalBottomSheet (modal) Material Design ใน Flutter เป็นไปตามหลักการเดียวกับใน Android — Sheet ปรากฏจากด้านล่าง รองรับการลาก และมีความสูงที่ปรับได้ สำหรับพฤติกรรมแบบขยายด้วย detents จะใช้ DraggableScrollableSheet
Scaffold.of(context).showBottomSheet((context) {
return Container(
padding: EdgeInsets.all(16),
child: Column(
children: [
Text("เนื้อหา Sheet"),
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: Text("ปิด")
)
]
)
);
})
React Native ไม่มีคอมโพเนนต์ Bottom Sheet ในตัวในแกนหลัก ดังนั้นนักพัฒนาจึงใช้ไลบรารีของบุคคลที่สาม ที่นิยมที่สุดคือ @gorhom/bottom-sheet (มากกว่า 7,000 ดาวบน GitHub, 2024) มี Bottom Sheet ที่ขับเคลื่อนด้วยท่าทางพร้อมรองรับ snap points ภาพเคลื่อนไหว และส่วนหัวแบบติดตาย ไลบรารีสร้างขึ้นบน Reanimated 2 และ Gesture Handler เพื่อประสิทธิภาพ 60 FPS
การใช้งานที่ถูกต้อง ของ Bottom Sheet ต้องการการปฏิบัติตามแนวทางของแพลตฟอร์มและความเข้าใจในสถานการณ์ที่ Sheet เหมาะสม มาดูคำแนะนำหลักและข้อผิดพลาดทั่วไปเมื่อออกแบบ Sheet
Bottom Sheet เหมาะสมที่สุด สำหรับการกระทำที่เป็นส่วนเสริมของเนื้อหาหลักบนหน้าจอ ตัวกรอง การเรียงลำดับ การเลือกตัวเลือก ข้อมูลสั้นๆ เกี่ยวกับรายการ — สถานการณ์ทั่วไป Material Design แนะนำให้ใช้ Bottom Sheet สำหรับเครื่องมือที่ไม่ควรกินพื้นที่ทั้งหน้าจอแต่ต้องการพื้นที่มากกว่า Action Sheet หรือ Popup Menu
อย่าใช้ Bottom Sheet สำหรับคำเตือนที่สำคัญ หรือข้อผิดพลาด — หน้าต่างโต้ตอบเหมาะสมกว่าสำหรับสิ่งนี้ หลีกเลี่ยง Sheet ที่มีช่องป้อนข้อมูลจำนวนมากที่ต้องเลื่อน: ผู้ใช้จะเข้าถึงแป้นพิมพ์ได้ยากและสูญเสียบริบท หาก Sheet มีรายการการกระทำมากกว่าเจ็ดรายการ ให้พิจารณาหน้าจอแยกต่างหากแทน Sheet
ข้อผิดพลาดทั่วไปคือการตั้งค่า peekHeight (ความสูงในสถานะยุบ) มากกว่า 40% ของหน้าจอ ผู้ใช้ไม่เข้าใจว่ากำลังเห็น Sheet ทั้งหมดหรือสามารถขยายได้ Material Design แนะนำ peekHeight ภายใน 15-30% ของความสูงหน้าจอเพื่อให้เนื้อหาส่วนสำคัญถูกซ่อนไว้และกระตุ้นให้ลาก
บน iOS ผู้ใช้คาดหวังว่า Sheet สามารถปิดได้โดยการปัดลงจากตำแหน่งใดๆ หากคุณปิดการใช้งานท่าทางนี้ (ผ่าน UISheetPresentationController.prefersEdgeAttachedInCompactHeight) ผู้ใช้จะรู้สึกหงุดหงิดเมื่อพยายามปิด Sheet ด้วยวิธีที่คุ้นเคย แนวทางอินเทอร์เฟซมนุษย์ของ Apple (2024) เน้นว่าการปัดเพื่อปิดเป็นความคาดหวังพื้นฐานของผู้ใช้ iOS
คำถามที่พบบ่อย
Bottom Sheet ไม่บล็อกพื้นหลังอย่างสมบูรณ์และรองรับการลาก ในขณะที่หน้าต่างโต้ตอบเป็น modal และต้องการการกระทำที่จำเป็น Bottom Sheet เหมาะกับตัวเลือกเสริมมากกว่า หน้าต่างโต้ตอบสำหรับการยืนยันที่สำคัญ
ได้ แต่ต้องตั้งค่า adjustResize หรือ adjustPan ใน Android Manifest บน iOS แป้นพิมพ์จะยก Sheet ขึ้นโดยอัตโนมัติ Flutter และ React Native ต้องการการจัดการด้วยตนเองผ่าน MediaQuery สำหรับการวางตำแหน่งที่ถูกต้อง
แนะนำ ไม่เกิน 5-7 รายการใน Sheet มาตรฐาน หากมีเนื้อหามากขึ้น ให้ใช้การเลื่อนหรือแบ่งหมวดหมู่ Material Design แนะนำอย่าให้เกิน 90% ของความสูงหน้าจอในสถานะ expanded
สำหรับ Sheet แบบ modal ให้ตั้ง setCancelable(true) บน Android หรือ isModalInPresentation = false บน iOS ใน Jetpack Compose onDismissRequest จัดการการแตะพื้นหลังโดยอัตโนมัติ ใน SwiftUI การปัดลงเป็นพฤติกรรมเริ่มต้น
ใช่ การนำไปใช้แบบดั้งเดิมทั้งหมดรองรับ VoiceOver และ TalkBack ตรวจสอบให้แน่ใจว่า FocusManager ย้ายโฟกัสไปที่ Sheet เมื่อเปิดและส่งกลับเมื่อปิด การนำไปใช้แบบกำหนดเองต้องการการกำหนดค่าการเข้าถึงด้วยตนเอง
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม