当前位置:   article > 正文

iOS 仿豆瓣电影详情页嵌套滑动效果

ios jxcategoryview如何使用demo

Python实战社群

Java实战社群

长按识别下方二维码,按需求添加

扫码关注添加客服

进Python社群▲

扫码关注添加客服

进Java社群


作者 | QuintGao 
来源 | 掘金,点击阅读原文查看作者更多文章

前言

之前写的GKPageScrollView https://github.com/QuintGao/GKPageScrollView 已经收获接近900个star,最近有时间对里面的GKPageSmoothView进行了优化升级,顺便实现了豆瓣电影详情页的效果,本文主要对GKPageSmoothView的实现做下简单介绍

先看下豆瓣电影的效果图

介绍

GKPageSmoothView与GKPageScrollView的实现原理不同
GKPageSmoothView主要是通过修改listScrollView的contentInset,然后添加headerView,并在滑动到一定程度的时候对headerView进行位置和布局的转换
GKPageScrollView主要是包含mainTableView和listScrollView,并在合适的位置处理滑动冲突

GKPageSmoothView支持以下功能:

  • 支持上下滑动、左右滑动、手势返回等

  • 支持顶部悬停、底部悬停

  • 支持底部悬停拖拽,可实现豆瓣电影详情页效果

  • 支持如JXCategoryView,JXSegmentedView等的分段控件

  • 可实现导航栏颜色渐变、头图下拉放大等效果

  • 支持主页下拉刷新、列表页上拉加载

实现

基本原理可以看这里UIScrollView嵌套滚动完美解决方案:仿淘宝、转转首页 下面主要讲讲底部悬停及底部拖拽的实现

底部悬停

底部悬停的实现跟顶部悬停的实现基本一样,前提是headerView的高度要高于屏幕的高度,这样当headerView下滑到可以悬浮的位置时将segmentedView添加到GKPageSmoothView上固定,不再跟随listScrollView滑动,下面看下主要代码

  1. // 滑动到临界位置,固定segmentedView
  2. if (contentOffsetY < (self.headerContainerHeight - self.frame.size.height)) {
  3. self.hoverType = GKPageSmoothHoverTypeBottom;
  4. if (self.segmentedView.superview != self.bottomContainerView) {
  5. self.bottomContainerView.hidden = NO;
  6. CGRect frame = self.segmentedView.frame;
  7. frame.origin.y = 0;
  8. self.segmentedView.frame = frame;
  9. [self.bottomContainerView addSubview:self.segmentedView];
  10. }
  11. }else {
  12. // 超过临界位置,segmentedView跟listScrollView一起滑动
  13. if (self.segmentedView.superview != self.headerContainerView) {
  14. self.bottomContainerView.hidden = YES;
  15. CGRect frame = self.segmentedView.frame;
  16. frame.origin.y = self.headerHeight;
  17. self.segmentedView.frame = frame;
  18. [self.headerContainerView addSubview:self.segmentedView];
  19. }
  20. }

底部拖拽

通过观察豆瓣详情页发现,拖拽底部时segmentedView和listScrollView是一起滑动的,所以添加了bottomContainerView来处理,并添加拖拽手势,当拖拽bottomContainerView时,让headerContainerView保持固定不动,segmentedView和listScrollView根据bottomContainerView一起滑动,此时只需处理拖拽手势和listScrollView的滑动即可

1、开始拖拽时,处理headerContainerView和listCollectionView

  1. // 将headerContainerView添加到self
  2. if (self.headerContainerView.superview != self) {
  3. CGRect frame = self.headerContainerView.frame;
  4. frame.origin.y = -(self.currentListScrollView.contentOffset.y + self.headerContainerHeight);
  5. self.headerContainerView.frame = frame;
  6. [self insertSubview:self.headerContainerView belowSubview:self.bottomContainerView];
  7. }
  8. // 将listCollectionView添加到bottomContainerView
  9. if (self.listCollectionView.superview != self.bottomContainerView) {
  10. CGRect frame = self.listCollectionView.frame;
  11. frame.origin.y = self.segmentedHeight;
  12. frame.size.height = self.bottomContainerView.frame.size.height - self.segmentedHeight;
  13. self.listCollectionView.frame = frame;
  14. [self.bottomContainerView addSubview:self.listCollectionView];
  15. self->_listCollectionView.headerContainerView = nil;
  16. // 记录当前列表的滑动位置
  17. self.currentListPanBeganContentOffsetY = self.currentListScrollView.contentOffset.y;
  18. for (id<GKPageSmoothListViewDelegate> list in self.listDict.allValues) {
  19. list.listScrollView.contentInset = UIEdgeInsetsZero;
  20. list.listScrollView.contentOffset = CGPointZero;
  21. CGRect frame = list.listView.frame;
  22. frame.size = self.listCollectionView.bounds.size;
  23. list.listView.frame = frame;
  24. }
  25. }

2、结束拖拽时,如果没有到达顶部,恢复到初始位置

  1. // headerContainerView添加到listHeader
  2. UIView *listHeader = [self listHeaderForListScrollView:self.currentListScrollView];
  3. if (self.headerContainerView.superview != listHeader) {
  4. CGRect frame = self.headerContainerView.frame;
  5. frame.origin.y = 0;
  6. self.headerContainerView.frame = frame;
  7. [listHeader addSubview:self.headerContainerView];
  8. }
  9. // listCollectionView添加到self
  10. if (self.listCollectionView.superview != self) {
  11. self.listCollectionView.frame = self.bounds;
  12. [self insertSubview:self.listCollectionView belowSubview:self.bottomContainerView];
  13. self->_listCollectionView.headerContainerView = self.headerContainerView;
  14. for (id<GKPageSmoothListViewDelegate> list in self.listDict.allValues) {
  15. list.listScrollView.contentInset = UIEdgeInsetsMake(self.headerContainerHeight, 0, 0, 0);
  16. list.listScrollView.contentOffset = CGPointZero;
  17. CGRect frame = list.listView.frame;
  18. frame.size = self.listCollectionView.bounds.size;
  19. list.listView.frame = frame;
  20. }
  21. self.currentListScrollView.contentOffset = CGPointMake(0, self.currentListPanBeganContentOffsetY);
  22. }

3、拖拽滑动处理

  1. - (void)handlePanGesture:(UIPanGestureRecognizer *)panGesture {
  2. //
  3. if (panGesture.state == UIGestureRecognizerStateBegan) {
  4. [self dragBegan];
  5. }
  6. CGPoint translation = [panGesture translationInView:self.bottomContainerView];
  7. if (self.isDragScrollView) {
  8. [self allowScrolling:self.scrollView];
  9. // 当UIScrollView在最顶部时,处理视图的滑动
  10. if (self.scrollView.contentOffset.y <= 0) {
  11. if (translation.y > 0) { // 向下拖拽
  12. [self forbidScrolling:self.scrollView];
  13. self.isDragScrollView = NO;
  14. CGRect frame = self.bottomContainerView.frame;
  15. frame.origin.y += translation.y;
  16. self.bottomContainerView.frame = frame;
  17. if (!self.isAllowDragScroll) {
  18. self.scrollView.panGestureRecognizer.enabled = NO;
  19. self.scrollView.panGestureRecognizer.enabled = YES;
  20. }
  21. }
  22. }
  23. }else {
  24. // 根据listScrollView的位置处理bottomContainerView的frame
  25. CGFloat offsetY = self.scrollView.contentOffset.y;
  26. CGFloat ceilPointY = self.ceilPointHeight;
  27. if (offsetY <= 0) {
  28. [self forbidScrolling:self.scrollView];
  29. if (translation.y > 0) { // 向下拖拽
  30. CGRect frame = self.bottomContainerView.frame;
  31. frame.origin.y += translation.y;
  32. self.bottomContainerView.frame = frame;
  33. }else if (translation.y < 0 && self.bottomContainerView.frame.origin.y > ceilPointY) { // 向上拖拽
  34. CGRect frame = self.bottomContainerView.frame;
  35. frame.origin.y = MAX((self.bottomContainerView.frame.origin.y + translation.y), ceilPointY);
  36. self.bottomContainerView.frame = frame;
  37. }
  38. }else {
  39. if (translation.y < 0 && self.bottomContainerView.frame.origin.y > ceilPointY) {
  40. CGRect frame = self.bottomContainerView.frame;
  41. frame.origin.y = MAX((self.bottomContainerView.frame.origin.y + translation.y), ceilPointY);
  42. self.bottomContainerView.frame = frame;
  43. }
  44. if (self.bottomContainerView.frame.origin.y > ceilPointY) {
  45. [self forbidScrolling:self.scrollView];
  46. }else {
  47. [self allowScrolling:self.scrollView];
  48. }
  49. }
  50. }
  51. // 拖拽结束,判断上滑还是下滑并确定是滑动到顶部还是底部
  52. if (panGesture.state == UIGestureRecognizerStateEnded) {
  53. CGPoint velocity = [panGesture velocityInView:self.bottomContainerView];
  54. if (velocity.y < 0) { // 上滑
  55. if (fabs(self.lastTransitionY) > 5 && self.isDragScrollView == NO) {
  56. [self dragShowing];
  57. }else {
  58. if (self.bottomContainerView.frame.origin.y > (self.ceilPointHeight + self.bottomContainerView.frame.size.height / 2)) {
  59. [self dragDismiss];
  60. }else {
  61. [self dragShowing];
  62. }
  63. }
  64. }else { // 下滑
  65. if (fabs(self.lastTransitionY) > 5 && self.isDragScrollView == NO && !self.scrollView.isDecelerating) {
  66. [self dragDismiss];
  67. }else {
  68. if (self.bottomContainerView.frame.origin.y > (self.ceilPointHeight + self.bottomContainerView.frame.size.height / 2)) {
  69. [self dragDismiss];
  70. }else {
  71. [self dragShowing];
  72. }
  73. }
  74. }
  75. [self allowScrolling:self.scrollView];
  76. self.isDragScrollView = NO;
  77. self.scrollView = nil;
  78. }
  79. [panGesture setTranslation:CGPointZero inView:self.bottomContainerView];
  80. self.lastTransitionY = translation.y;
  81. }

底部拖拽实现基本就这些了,当然还有很多细节,想了解的可以查看具体代码。

使用

1、初始化GKPageSmoothView

  1. self.smoothView = [[GKPageSmoothView alloc] initWithDataSource:self];
  2. [self.view addSubView:self.smoothView];

2、初始化headerView和segmentedView

  1. self.headerView = [UIView new];
  2. self.segmentedView = [JXCategoryView new];

实现GKPageSmoothViewDataSource代理方法

  1. #pragma mark - GKPageSmoothViewDataSource
  2. - (UIView *)headerViewInSmoothView:(GKPageSmoothView *)smoothView {
  3. return self.headerView;
  4. }
  5. - (UIView *)segmentedViewInSmoothView:(GKPageSmoothView *)smoothView {
  6. return self.segmentedView;
  7. }
  8. - (NSInteger)numberOfListsInSmoothView:(GKPageSmoothView *)smoothView {
  9. return 2;
  10. }
  11. - (id<GKPageSmoothListViewDelegate>)smoothView:(GKPageSmoothView *)smoothView initListAtIndex:(NSInteger)index {
  12. GKDBListView *listView = [GKDBListView new];
  13. return listView;
  14. }

列表实现GKPageSmoothListViewDelegate代理方法

  1. #pragma mark - GKPageSmoothListViewDelegate
  2. - (UIScrollView *)listScrollView {
  3. return self.tableView;
  4. }
  5. - (UIView *)listView {
  6. return self;
  7. }

其他常用属性

顶部临界高度

self.smoothView.ceilPointHeight = NAVBAR_HEIGHT;

开启底部悬浮

self.smoothView.bottomHover = YES;

允许底部拖拽

self.smoothView.allowDragBottom = YES;

delegate常用方法代理

  1. self.smoothView.delegate = self;
  2. /// 列表容器滑动代理
  3. /// @param smoothView smoothView
  4. /// @param scrollView containerScrollView
  5. - (void)smoothView:(GKPageSmoothView *)smoothView scrollViewDidScroll:(UIScrollView *)scrollView;
  6. /// 当前列表滑动代理
  7. /// @param smoothView smoothView
  8. /// @param scrollView 当前的列表scrollView
  9. /// @param contentOffset 转换后的contentOffset
  10. - (void)smoothView:(GKPageSmoothView *)smoothView listScrollViewDidScroll:(UIScrollView *)scrollView contentOffset:(CGPoint)contentOffset;
  11. /// 开始拖拽代理
  12. /// @param smoothView smoothView
  13. - (void)smoothViewDragBegan:(GKPageSmoothView *)smoothView;
  14. /// 结束拖拽代理
  15. /// @param smoothView smoothView
  16. /// @param isOnTop 是否通过拖拽滑动到顶部
  17. - (void)smoothViewDragEnded:(GKPageSmoothView *)smoothView isOnTop:(BOOL)isOnTop;

结语

至此,GKPageSmoothView的介绍已经完成,如果你想了解更多可以查看源码GKPageSmoothView,如果您觉得还不错,可以点个star,您的支持是我最大的动力。

参考

[1]UIScrollView嵌套滚动完美解决方案:仿淘宝、转转首页 https://www.jianshu.com/p/7b1a3feba5a3
[2]iOS仿抖音—评论视图滑动消失 https://www.jianshu.com/p/8a1f174a91e5

  1. 程序员专栏 扫码关注填加客服 长按识别下方二维码进群

近期精彩内容推荐:  

 看电影前一定要检查一下域名是不是HTTPS的

 有个大神级女朋友是什么体验

 世界上五个最不务正业的科学家!

 魂斗罗只有128KB为何可以实现那么长的剧情

在看点这里好文分享给更多人↓↓

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号