| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- //
- // HistoryPreloader.m
- // AIIM
- //
- // Created by qitewei on 2025/5/20.
- //
- #import "HistoryPreloader.h"
- #import <objc/runtime.h>
- @interface HistoryPreloader() {
- CADisplayLink *_displayLink;
- CGFloat _initialOffset;
- BOOL _isUserInitiatedScroll;
- NSTimeInterval _lastTriggerTime;
- CGFloat _lastScrollVelocity;
- }
- @end
- @implementation HistoryPreloader
- - (instancetype)init {
- self = [super init];
- if (self) {
- _threshold = 300.0; // 默认阈值
- _initialOffset = CGFLOAT_MAX;
- }
- return self;
- }
- - (void)dealloc {
- [self stopMonitoring];
- }
- #pragma mark - 公共方法
- - (void)startMonitoring {
- if (_displayLink) return;
-
- // 记录初始偏移量(考虑contentInset)
- _initialOffset = -self.tableView.contentInset.top;
- _isUserInitiatedScroll = NO;
-
- // 设置DisplayLink监控滚动
- _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleScrollCheck)];
- [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
- }
- - (void)stopMonitoring {
- [_displayLink invalidate];
- _displayLink = nil;
- }
- - (void)resetScrollState {
- _isUserInitiatedScroll = NO;
- _initialOffset = -self.tableView.contentInset.top;
- }
- #pragma mark - 私有方法
- - (void)handleScrollCheck {
- CGFloat currentOffset = self.tableView.contentOffset.y;
-
- // 1. 检测是否是用户发起的滑动
- if (!_isUserInitiatedScroll) {
- // 使用5pt的阈值避免误判
- if (fabs(currentOffset - _initialOffset) > 5.0) {
- _isUserInitiatedScroll = YES;
- } else {
- return; // 未滑动,直接返回
- }
- }
-
- // 2. 计算滚动速度(用于动态调整阈值)
- CFTimeInterval timestamp = _displayLink.timestamp;
- static CFTimeInterval lastTimestamp = 0;
- if (lastTimestamp != 0) {
- _lastScrollVelocity = (currentOffset - _lastScrollVelocity) / (timestamp - lastTimestamp);
- }
- lastTimestamp = timestamp;
- _lastScrollVelocity = currentOffset;
-
- // 3. 动态调整阈值(可选)
- CGFloat dynamicThreshold = [self dynamicThresholdBasedOnVelocity:_lastScrollVelocity];
-
- // 4. 检查是否需要触发预加载
- if (currentOffset < dynamicThreshold) {
- [self tryTriggerPreload];
- }
- }
- - (CGFloat)dynamicThresholdBasedOnVelocity:(CGFloat)velocity {
- // 基础阈值
- CGFloat baseThreshold = self.threshold;
-
- // 根据滚动速度调整(速度越快,阈值越大,提前触发)
- if (fabs(velocity) > 100) { // 快速滚动
- return baseThreshold * 1.5;
- } else if (fabs(velocity) < 20) { // 慢速滚动
- return baseThreshold * 0.8;
- }
- return baseThreshold;
- }
- - (void)tryTriggerPreload {
- // 冷却时间检查(至少间隔1秒)
- NSTimeInterval now = [NSDate timeIntervalSinceReferenceDate];
- if (now - _lastTriggerTime < 1.0){
- // 触发过快加载加载回调
- if (self.toofast) {
- self.toofast();
- }
- return;
- }
-
- _lastTriggerTime = now;
-
- // 触发加载回调
- if (self.loadBlock) {
- self.loadBlock();
- }
-
- // 短暂停止监控防止连续触发
- [self stopMonitoring];
- __weak typeof(self) weakSelf = self;
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
- [weakSelf startMonitoring];
- });
- }
- #pragma mark - 只读属性访问
- - (BOOL)isUserInitiatedScroll {
- return _isUserInitiatedScroll;
- }
- @end
|