产品提了一个需求, 有一个屏幕浮层, 里面有个列表, 滑动列表时候整个分层要能跟着在屏幕上挪动, 同时浮层上方还有个拖动区域, 也要能拖曳, 我们知道 UITableView
自己本身可以相应滚动手势, 列表上方可以通过一个UIPanGestureRecognizer
手势来拖曳, 但是怎么让两者之间相互适配, 更丝滑的实现滚动效果, 还是花了很多时间, 处理的细节比较多, 比如:
- 顶部的滚动手势没必要处理跟 UITableView 的手势冲突, 按照下面处理也可以跟手
UITableView
响应手势的时候, 可以通过scrollViewDidScroll
设置整个浮层的 y 值- 列表之外通过
UIPanGestureRecognizer
响应手势, 并在合适的时候设置UITableView
的 contentOffsetY.
- 快速滑动可以直接动画, 不用跟手了.
UITableView
通过hs_scrollViewWillEndDragging
的velocity
来识别快速滑动UIPanGestureRecognizer
手势结束的时候, 可以通过[gesture velocityInView:self]
判断速度, 来识别快速滑动
- 滑动结束的时候, 如果是快速滑动, 根据速度方向, 以及当前浮层的 y 值快速动画到下一个状态, 否则直接根据y 值进入下一个状态
UITableView
响应手势的时候, 如果浮层 y值可以动, 就通过[tableView setContentOffset: animated:NO];
不让UITableView
产生滚动, 当浮层不能上下动再让UITableView
产生滚动- 需要通过
UITableView
的回调判定用户当前是否还在操作屏幕, 此时要跟手
大概效果如下:
封装实现:WYUtils/Classes/UIView/UIView+WYHalfScreen.h
参考demo Example/SubItems/View/WYHalfScreenAlertViewController.m
接入
- 导入相关文件并
import
头文件 - 给浮层 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 - 将上面定义的几个组件绑定下, 并设定屏幕高度/ 半屏高度/最大高度
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];
} - 实现 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
- (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];
}
} - 实现展开动画以及结束动画
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];
}];
}