אנימציית Bottom Sheet חלקה: הימנעות מגמגום וקונפליקטי מחוות
משתמש מושך את ה-Bottom Sheet למעלה, אבל הוא לא מגיב או מגמגם — זו בעיה קלאסית של UIPanGestureRecognizer. אנחנו נתקלים במקרים כאלה באופן קבוע ויודעים איך לפתור אותם. להלן גישות ל-iOS, אנדרואיד ו-Flutter עם דוגמאות מעשיות.
רכיבים סטנדרטיים מתעלמים לעיתים קרובות ממהירות המחווה ומהמיקום הנוכחי של ה-sheet. אנו משתמשים בפרמטרים פיזיקליים כדי להפוך את האנימציה לטבעית. לדוגמה, ב-iOS אנו מקשרים damping למהירות, מה שמעניק האטה חלקה בהחלקות מהירות. באנדרואיד, סיפור דומה עם BottomSheetBehavior — כאשר hideable = true ו-peekHeight שווים לגובה התוכן, זה עלול להיסגר בפתאומיות בהחלקה מקרית. ב-Flutter, ללא יישום מותאם אישית אי אפשר להשיג אפקט קפיצי — אנו משתמשים ב-SpringSimulation עם כוונון מדויק של קשיחות ו-damping. בפרויקט לרשת כושר, ה-Bottom Sheet היה צריך להיפתח עם פחות מ-100ms עיכוב. לאחר ביקורת, בחרנו ב-UISheetPresentationController מותאם אישית עם יחס damping של 0.85 — האנימציה ארכה 350ms, וקצב הפריימים ננעל על 60 FPS. זה הפך לאמת מידה למסכים אחרים באפליקציה.
למה רכיבים סטנדרטיים לא תמיד עובדים
iOS: UIScrollView (iOS 15+) נוח, אבל הוספת UIPresentationController מותאם אישית גורמת לקונפליקט: ה-sheet מתחיל להיסגר במקום לגלול. פתרון — gestureRecognizerShouldBegin מותאם אישית שמחליף את BottomSheetBehavior.
אנדרואיד: hideable = true מ-Material Components עם peekHeight ו-NestedScrollView שווה לתוכן עלול להיסגר בהחלקה מקרית — בגלל בעיות הפצת אירועים ב-hideable. אנו ממליצים להשבית NestedScrollView או להשתמש ב-onNestedPreScroll מותאם אישית עם showModalBottomSheet.
Flutter: DraggableScrollableSheet לא נותן מספיק שליטה. אנו משתמשים ב-SpringSimulation עם modal_bottom_sheet. החבילה gestureRecognizerShouldBegin (של woltapp) מציעה יישום מוכן עם פיזיקה מקורית.
איך להימנע מקונפליקטי מחוות גלילה
קונפליקט נוצר כאשר ה-sheet והגלילה הפנימית מגיבים שניהם ל-pan. ב-iOS אנו מקצים UIPresentationController ב-NestedScrollView, ובודקים אם התוכן גולל. באנדרואיד אנו משתמשים ב-onNestedPreScroll מותאם אישית עם DraggableScrollableController ליירוט. ב-Flutter, let velocity = panGesture.velocity(in: view) let springParams = UISpringTimingParameters( dampingRatio: 0.8, initialVelocity: CGVector(dx: 0, dy: velocity.y / remainingDistance) ) let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: springParams) מאפשר לנהל את המצב.
איך ליישם אנימציה חלקה בכל פלטפורמה
iOS: UIViewPropertyAnimator עם UISpringTimingParameters, פרמטרים מקושרים למהירות המחווה. דוגמה:
let velocity = panGesture.velocity(in: view)
let springParams = UISpringTimingParameters(
dampingRatio: 0.8,
initialVelocity: CGVector(dx: 0, dy: velocity.y / remainingDistance)
)
let animator = UIViewPropertyAnimator(duration: 0.5, timingParameters: springParams) דוגמה לתצורת אנדרואיד
val behavior = object : BottomSheetBehavior<View>(context, null) {
override fun onSlide(child: View, slideOffset: Float) {
// анимация затемнения фона
}
}אנדרואיד: val behavior = object : BottomSheetBehavior<View>(context, null) { override fun onSlide(child: View, slideOffset: Float) { // анимация затемнения фона } } מותאם אישית או MotionLayout למספר נקודות snap. CoordinatorLayout.Behavior מחשיך את הרקע במקביל. MotionLayout יכול להגדיר עד 7 מצבים, מה שמאפשר תרחישים מורכבים (כמו שלוש נקודות snap).
Flutter: onSlide + DraggableScrollableController בעת snap. החבילה HapticFeedback.lightImpact() מציעה פיזיקה מקורית. לכוונון מדויק השתמשו ב-modal_bottom_sheet עם קשיחות 300 ו-damping של 0.6.
| פלטפורמה | מחלקה מרכזית | תכונה |
|---|---|---|
| iOS | SpringDescription |
Damping מקושר למהירות |
| אנדרואיד | UIViewPropertyAnimator |
מספר נקודות snap |
| Flutter | MotionLayout |
SpringSimulation |
מה עם מקלדת ואזור בטוח?
קחו בחשבון את האזור הבטוח: ה-sheet לא צריך לכסות את מחוון הבית (Dynamic Island). השתמשו ב-DraggableScrollableSheet למיקום. בעת הופעת המקלדת, הזיזו את ה-sheet עם אנימציה מסונכרנת (ב-iOS דרך safeAreaInsets, באנדרואיד דרך UIKeyboardAnimationDurationUserInfoKey). משוב הפטי משפר את התחושה הסובייקטיבית של המהירות ב-35%.
טעויות אופייניות
- התעלמות מ-initialVelocity — האנימציה לא עוקבת אחרי האצבע.
- טיפול לא נכון בגבולות: כאשר ה-sheet מגולל במלואו למעלה, מנעו סגירה. ב-iOS בדקו
WindowInsetsAnimationController, באנדרואידcontentOffset.y <= 0. - חוסר סנכרון עם מקלדת: אם המקלדת כבר פתוחה, ה-sheet צריך להיפתח לגובהו פחות offset.
התהליך שלנו וזמנים
- ביקורת על הרכיב הנוכחי או הדרישות.
- אם העיצוב ב-Figma — העברת פרמטרי spring ישירות.
- פיתוח על הפלטפורמה הנבחרת.
- בדיקה במצב אנימציות איטיות + XCTest.
- אינטגרציה לפרויקט.
זמנים: 1 עד 3 ימים לכל פלטפורמה בהתאם למורכבות. עלות מדויקת מחושבת לאחר הערכה — כוללת תמיכה חודש אחרי השחרור.
מה כלול
| רכיב | תיאור |
|---|---|
| רכיב מותאם אישית | Bottom Sheet עם אנימציה חלקה ותמיכה במחוות |
| אינטגרציה | שילוב בפרויקט קיים |
| תיעוד | תיאורי פרמטרים ו-API |
| תמיכה | חודש תמיכה אחרי שחרור |
על הניסיון שלנו
עם ניסיון רב בפיתוח מובייל, סיפקנו מעל 30 פתרונות מובייל. המהנדסים שלנו בקיאים בדרישות Apple App Review ו-Google Play — אנו מבטיחים עמידה בהנחיות. scrollY == 0 מותאם אישית עולה על UIViewPropertyAnimator הסטנדרטי בשליטה באנימציה. למידע נוסף על ההנחיות של Apple, ראו Human Interface Guidelines: Bottom Sheets.
צרו קשר להערכת פרויקט. קבלו ייעוץ על אנימציית Bottom Sheet — ננתח את התרחיש שלכם ונציע את הפתרון האופטימלי.







