Flutter中如何实现WhatsApp移动端首页滚动效果
iOS实现WhatsApp首页滚动交互方案
这个交互的核心逻辑是滚动事件分层响应,不要把搜索栏和列表做滚动绑定,把搜索栏作为独立视图单独控制滚动响应优先级,就能实现「先藏搜索栏,再滚列表内容」的效果。
UIKit 实现(和WhatsApp iOS端原生技术栈一致)
1. 视图层级搭法
别把搜索栏设为UITableView/UICollectionView的tableHeaderView,那种方式会让搜索栏和列表强制同步滚动,做不出分段效果。正确层级:
- 父视图上先放聊天列表,给列表设置顶部
contentInset,值等于搜索栏高度(通常是52pt),给搜索栏留初始位置 - 搜索栏作为独立子视图,加在列表的上层视图里,初始位置紧贴导航栏底部,frame高度和你设置的列表顶部inset保持一致
2. 核心滚动逻辑
实现列表的scrollViewDidScroll代理,把滚动拆成三个阶段处理,核心代码如下:
// 搜索栏固定高度 let searchBarHeight: CGFloat = 52 func scrollViewDidScroll(_ scrollView: UIScrollView) { // 计算去掉安全区/导航栏后的真实滚动偏移 let realOffsetY = scrollView.contentOffset.y + scrollView.adjustedContentInset.top switch realOffsetY { // 下拉到顶部以上的情况:搜索栏完全复位 case ..<0: searchBar.transform = .identity searchBar.isUserInteractionEnabled = true // 上滑偏移量在0~搜索栏高度区间:只动搜索栏,锁死列表滚动 case 0..<searchBarHeight: // 强制把列表偏移钉在初始位置,不让列表内容上移 scrollView.contentOffset.y = -scrollView.adjustedContentInset.top // 搜索栏跟随手指滑动距离上移 searchBar.transform = CGAffineTransform(translationX: 0, y: -realOffsetY) searchBar.isUserInteractionEnabled = false // 偏移量超过搜索栏高度:搜索栏完全隐藏,放开列表正常滚动 default: searchBar.transform = CGAffineTransform(translationX: 0, y: -searchBarHeight) searchBar.isUserInteractionEnabled = false } }
3. 体验优化细节
- 松手吸附效果:在
scrollViewDidEndDragging代理里判断当前偏移,如果半隐藏状态下偏移没到搜索栏高度的一半,就动画复位搜索栏;超过一半就动画把搜索栏完全隐藏,避免搜索栏停在半露的位置 - 不要直接用系统
UINavigationItem.searchController,系统自带搜索栏和导航栏大标题强绑定联动,没法实现独立隐藏的分段效果 - 适配导航栏大标题场景时,记得把大标题的高度算进初始偏移基准,避免位置错位
SwiftUI 实现思路
逻辑和UIKit完全一致,只是获取滚动偏移的方式不同:
- 用
ZStack叠放自定义搜索栏和聊天列表,给列表设置顶部内边距预留搜索栏位置 - 通过
PreferenceKey+GeometryReader读取列表实时滚动偏移 - 偏移量在0到搜索栏高度区间时,只移动搜索栏位置,固定列表初始偏移;搜索栏完全隐藏后再放开列表正常滚动
避坑:不要用SwiftUI自带的
.searchablemodifier,和系统UIKit的搜索控制器一样,是绑定导航栏联动的,没法实现分段滚动效果。
内容的提问来源于stack exchange,提问作者Samuel
相关产品推荐
相关产品推荐

