iOS 半屏浮层展示列表并支持滑动的快速实现

产品提了一个需求, 有一个屏幕浮层, 里面有个列表, 滑动列表时候整个分层要能跟着在屏幕上挪动, 同时浮层上方还有个拖动区域, 也要能拖曳, 我们知道 UITableView 自己本身可以相应滚动手势, 列表上方可以通过一个UIPanGestureRecognizer手势来拖曳, 但是怎么让两者之间相互适配, 更丝滑的实现滚动效果, 还是花了很多时间, 处理的细节比较多, 比如:

  1. 顶部的滚动手势没必要处理跟 UITableView 的手势冲突, 按照下面处理也可以跟手
    1. UITableView 响应手势的时候, 可以通过 scrollViewDidScroll 设置整个浮层的 y 值
    2. 列表之外通过 UIPanGestureRecognizer响应手势, 并在合适的时候设置 UITableView 的 contentOffsetY.
  2. 快速滑动可以直接动画, 不用跟手了.
    1. UITableView 通过 hs_scrollViewWillEndDraggingvelocity 来识别快速滑动
    2. UIPanGestureRecognizer手势结束的时候, 可以通过[gesture velocityInView:self]判断速度, 来识别快速滑动
  3. 滑动结束的时候, 如果是快速滑动, 根据速度方向, 以及当前浮层的 y 值快速动画到下一个状态, 否则直接根据y 值进入下一个状态
  4. UITableView 响应手势的时候, 如果浮层 y值可以动, 就通过[tableView setContentOffset: animated:NO];不让 UITableView 产生滚动, 当浮层不能上下动再让UITableView 产生滚动
  5. 需要通过UITableView的回调判定用户当前是否还在操作屏幕, 此时要跟手

大概效果如下:
Jun-28-2024 19-41-02

封装实现:WYUtils/Classes/UIView/UIView+WYHalfScreen.h

参考demo Example/SubItems/View/WYHalfScreenAlertViewController.m

接入

  1. 导入相关文件并 import 头文件
  2. 给浮层 View 实现相关代理, 并添加相应手势的 View 以及 UITableView
    1
    2
    3
    4
    5
    6
    @interface WYHalfScreenView () <UITableViewDataSource, UITableViewDelegate, UIGestureRecognizerDelegate>

    @property (nonatomic, strong) UIView *topPanView;
    @property (nonatomic, strong) UITableView *tableView;

    @end
  3. 将上面定义的几个组件绑定下, 并设定屏幕高度/ 半屏高度/最大高度
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    - (void)configGesture
    {
    WY_WEAK_SELF(self);
    [self hs_commonInitialWithDragView:self.topPanView
    dragDelegate:self
    scrollView:self.tableView
    hideCompletion:^{
    WY_STRONG_SELF(self);
    [self hide];
    }];
    self.hs_viewHeight = WY_SCREEN_HEIGHT;
    self.hs_minHeight = [self.class actionSheetViewMinHeight];
    self.hs_maxHeight = [self.class actionSheetViewMaxHeight];
    }
  4. 实现 UIScrollViewDelegate 并调用几个方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    #pragma mark - UITableViewDelegate

    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
    if (scrollView == self.tableView)
    {
    [self hs_scrollViewDidScroll:scrollView];
    }
    }

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
    {
    if (scrollView == self.tableView)
    {
    [self hs_scrollViewWillBeginDragging:scrollView];
    }
    }

    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
    if (scrollView == self.tableView)
    {
    [self hs_scrollViewDidEndDecelerating:scrollView];
    }
    }

    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
    {
    if (scrollView == self.tableView)
    {
    [self hs_scrollViewDidEndDragging:scrollView willDecelerate:decelerate];
    }
    }

    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
    {
    if (scrollView == self.tableView)
    {
    [self hs_scrollViewWillEndDragging:scrollView
    withVelocity:velocity
    targetContentOffset:targetContentOffset];
    }
    }
  5. 实现展开动画以及结束动画
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    - (void)show
    {
    // 不能直接在 VC 上弹出, 二级页边缘手势处理不了, 跟歌曲AS一样直接在 UIWindow 上弹出吧
    UIWindow *window = [[UIApplication sharedApplication] keyWindow];
    [window addSubview:self];
    WY_WEAK_SELF(self);
    [self hs_show:^{
    WY_STRONG_SELF(self);
    [self setBackgroundColor:[[UIColor blackColor] colorWithAlphaComponent:0.4]];
    } completion:^{

    }];
    }

    - (void)hide
    {
    WY_WEAK_SELF(self);
    [self hs_hide:^{
    WY_STRONG_SELF(self);
    [self setBackgroundColor:[UIColor clearColor]];
    } completion:^{
    WY_STRONG_SELF(self);
    [self removeFromSuperview];
    }];
    }
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道