- (void)scrollViewWillEndDragging:(UITableView *)tableView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
CGPoint offset = tableView.contentOffset;
// 临界值处理
BOOL hasScrollToTop = offset.y <= -tableView.contentInset.top;
BOOL hasScrollToBottom = offset.y >= tableView.contentSize.height - tableView.bounds.size.height + tableView.contentInset.bottom;
if (hasScrollToTop || hasScrollToBottom) return;
// NSLog(@"offset:%@ velocity:%@", NSStringFromCGPoint(offset), NSStringFromCGPoint(velocity));
CGFloat velocityY = velocity.y;
CGFloat panGestureVelocityY = [tableView.panGestureRecognizer velocityInView:self.view].y;
NSLog(@"velocityY2:%.2f", panGestureVelocityY);
if (velocityY > 0) {
NSLog(@"松手时上滑,速度:%.2f", velocityY);
} else if (velocityY < 0) {
NSLog(@"松手时下滑,速度:%.2f", velocityY);
} else {
NSLog(@"松手时手静止");
}
NSArray<TableViewCell *> *visibleCells = tableView.visibleCells;
NSUInteger firstMin = NSUIntegerMax;
NSUInteger secondMin = NSUIntegerMax;
for (TableViewCell *cell in visibleCells) {
NSUInteger value = cell.model.index;
if (value < firstMin) { // 小于最小的元素 更新 1 和 2
secondMin = firstMin;
firstMin = value;
} else if (value < secondMin && value != firstMin) { // 小于倒数二的 更新 2
secondMin = value;
}
}
TableViewCell *topCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:firstMin inSection:0]];
TableViewCell *secondCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:secondMin inSection:0]];
NSIndexPath *topCellIndexPath = [self.tableView indexPathForCell:topCell];
NSIndexPath *secondCellIndexPath = [self.tableView indexPathForCell:secondCell];
if (topCellIndexPath) {
if (!secondCellIndexPath) secondCellIndexPath = topCellIndexPath;
CGRect topCellRect = [tableView rectForRowAtIndexPath:topCellIndexPath];
CGRect secondCellRect = [tableView rectForRowAtIndexPath:secondCellIndexPath];
CGRect topCellRectToTableView = [topCell convertRect:topCell.bounds toView:self.tableViewMirrorView];
CGFloat topCellHalfHeight = CGRectGetHeight(topCellRectToTableView) * 0.5;
CGFloat thresholdValue = 50;
if (thresholdValue > topCellHalfHeight) {
thresholdValue = topCellHalfHeight;
}
NSLog(@"topCell rect:%@", NSStringFromCGRect(topCellRect));
NSLog(@"topCellRectToTableView rect:%@ ", NSStringFromCGRect(topCellRectToTableView));
// 最上面的 cell 顶部是否可见
BOOL isTopCellYVisable = topCellRectToTableView.origin.y > 0;
if (velocityY == 0) {
if (isTopCellYVisable) {
// 最上面的 cell 此 rect 大于 0 说明 是 headerView 在显示,或者内边距之内
targetContentOffset->y = topCellRect.origin.y;
} else {
// 是否划过了阈值
BOOL isPastThresholdValue = fabs(topCellRectToTableView.origin.y) > thresholdValue;
if (panGestureVelocityY > 0) {
// 停止之前是下滑
isPastThresholdValue = (topCellRectToTableView.size.height - fabs(topCellRectToTableView.origin.y)) > thresholdValue;
if (isPastThresholdValue) {
targetContentOffset->y = topCellRect.origin.y;
} else {
targetContentOffset->y = secondCellRect.origin.y;
}
} else if (panGestureVelocityY < 0) {
// 停止之前是上滑
isPastThresholdValue = fabs(topCellRectToTableView.origin.y) > thresholdValue;
if (isPastThresholdValue) {
targetContentOffset->y = secondCellRect.origin.y;
} else {
targetContentOffset->y = topCellRect.origin.y;
}
} else {
if (isPastThresholdValue) {
targetContentOffset->y = secondCellRect.origin.y;
} else {
targetContentOffset->y = topCellRect.origin.y;
}
}
}
} else if (velocityY > 1) { // 上滑很快,直接下一个
if (isTopCellYVisable) {
// 最上面的 cell 此 rect 大于 0 说明 是 headerView 在显示,或者内边距之内
targetContentOffset->y = topCellRect.origin.y;
} else {
targetContentOffset->y = secondCellRect.origin.y;
}
} else if (velocityY > 0) {
if (isTopCellYVisable) {
// 最上面的 cell 此 rect 大于 0 说明 是 headerView 在显示,或者内边距之内
targetContentOffset->y = topCellRect.origin.y;
} else {
// 是否划过了阈值
BOOL isPastThresholdValue = fabs(topCellRectToTableView.origin.y) > thresholdValue;
if (isPastThresholdValue) {
targetContentOffset->y = secondCellRect.origin.y;
} else {
targetContentOffset->y = topCellRect.origin.y;
}
}
} else if (velocityY < -0.8) { // 下滑很快,直接上一个
if (isTopCellYVisable) {
targetContentOffset->y = 0;
} else {
targetContentOffset->y = topCellRect.origin.y;
}
} else { // 其它速度下滑
if (isTopCellYVisable) {
targetContentOffset->y = 0;
} else {
// 是否划过了阈值
BOOL isPastThresholdValue = (topCellRectToTableView.size.height - fabs(topCellRectToTableView.origin.y)) > thresholdValue;
if (isPastThresholdValue) {
targetContentOffset->y = secondCellRect.origin.y;
} else {
targetContentOffset->y = topCellRect.origin.y;
}
}
}
}
}
#pragma mark - lazy load
- (UITableView *)tableView {
if (!_tableView) {
_tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
_tableView.backgroundColor = UIColor.whiteColor;
// 以下三项是适配iOS 11刷新时会漂移的情况
_tableView.estimatedSectionHeaderHeight = 0;
_tableView.estimatedSectionFooterHeight = 0;
_tableView.estimatedRowHeight = 0;
_tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
_tableView.showsVerticalScrollIndicator = NO;
_tableView.showsHorizontalScrollIndicator = NO;
_tableView.sectionFooterHeight = 0;
_tableView.sectionHeaderHeight = 0;
_tableView.alwaysBounceVertical = true;
UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 150)];
headerView.backgroundColor = [UIColor redColor];
_tableView.tableHeaderView = headerView;
_tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, CGFLOAT_MIN)];
_tableView.decelerationRate = UIScrollViewDecelerationRateFast;
if (@available(iOS 11.0, *)) {
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
if (@available(iOS 13.0, *)) {
_tableView.automaticallyAdjustsScrollIndicatorInsets = false;
}
}
return _tableView;
}
- (UIView *)tableViewMirrorView {
if (_tableViewMirrorView) return _tableViewMirrorView;
UIView *view = [[UIView alloc] init];
view.frame = self.tableView.frame;
view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin;
_tableViewMirrorView = view;
return _tableViewMirrorView;
}