UIScrollViewDelegate的几种特殊情况
UIScrollViewDelegate的几种特殊情况

UIScrollViewDelegate的几种特殊情况

常见的一些UIScrollViewDelegate代理回调可以参考https://www.cnblogs.com/cchHers/p/12423838.html,下面列出的主要是一些特殊情况。

1. scrollViewWillBeginDecelerating和scrollViewDidEndDecelerating并不是必须的

你可以尝试使用后附的代码在模拟器或手机上进行测试:当你手指接触屏幕并拖动一段距离,稍作停顿再松手,你会发现只执行到
scrollViewWillEndDraggingscrollViewDidEndDragging就停止了。

因此,除非你确定你的滑动列表一定会有减速动画(比如,启用了Paging且保证用户不会精准停留至无需结束弹性动画的位置),你可以尝试以scrollViewWillBeginDeceleratingscrollViewDidEndDecelerating作为滑动结束的标志,否则请不要将这两个回调作为唯一结束标志。

2. 区别手指滑动和命令滑动

如果你通过命令控制UIScrollView的contentOffset,比如

[self.collectionView setContentOffset:CGPointZero animated:YES];

你需要注意,通过setContentOffset方式进行滚动时会有下面几种情况

Animated: YESAnimated: NO
ContentOffset与上次相同
ContentOffset与上次不同[scrollViewDidScroll] 多次
[scrollViewDidEndScrollingAnimation] 一次
[scrollViewDidScroll] 一次

你应该注意到涉及到手指滑动的一些回调一律不会被调用,只会涉及到scrollViewDidScrollscrollViewDidEndScrollingAnimation这两种。如果你的App中存在通过命令滚动的情况则需要特别关心。

3. 轻点Status Bar至顶

点击Status Bar是一种比较tricky的操作,用户可以通过这种方式快速滚动至页面的顶端,它只会调用scrollViewDidScrollscrollViewDidScrollToTop这两种,并且scrollViewDidScrollToTop的触发时机是在滚动结束后,类似于scrollViewDidEndDecelerating。在一些需要对用户滑动监控的场景下(比如回到顶部时刷新信息流)同样需要进行特别关注,因为在这种场景下监听scrollViewDidEndDecelerating是无效的。

附录

可以使用下面的代码自行测试

#import "UIKit/UIkit.h"
#import "ViewController.h"

@interface ViewController () <UICollectionViewDelegate, UICollectionViewDataSource, UIScrollViewDelegate>

@property (nonatomic, strong) UITextView *logTextView;
@property (nonatomic, strong) UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray *logMessages;
@property (nonatomic, strong) UIButton *backToTopAnimatedButton;
@property (nonatomic, strong) UIButton *backToTopNoAnimationButton;
@property (nonatomic, strong) UIButton *scrollToRandomOffsetAnimatedButton;
@property (nonatomic, strong) UIButton *scrollToRandomOffsetNoAnimationButton;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    // Setup logTextView
    self.logTextView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 300)];
    self.logTextView.editable = NO;
    [self.view addSubview:self.logTextView];

    // Initialize logMessages array
    self.logMessages = [NSMutableArray array];

    // Setup UICollectionView
    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    layout.itemSize = CGSizeMake(100, 100); // Set the size of each cell

    self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 300, self.view.frame.size.width, self.view.frame.size.height - 300) collectionViewLayout:layout];
    self.collectionView.delegate = self;
    self.collectionView.dataSource = self;
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"];
//    [self.collectionView setPagingEnabled:YES];
    [self.view addSubview:self.collectionView];
    
    // Setup buttons
    [self setupButtons];
}

// UICollectionView DataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 100;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
    
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:cell.contentView.bounds];
    imageView.image = [UIImage imageNamed:@"example_image"]; // Use your image name here
    [cell.contentView addSubview:imageView];

    return cell;
}

// UIScrollViewDelegate Methods

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [self logMessage:@"[scrollViewDidScroll]"];
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [self logMessage:@"[scrollViewWillBeginDragging]"];
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    [self logMessage:@"[scrollViewWillEndDragging]"];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
    [self logMessage:@"[scrollViewDidEndDragging]"];
}

- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView {
    [self logMessage:@"[scrollViewShouldScrollToTop]"];
    return YES;
}

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView {
    [self logMessage:@"[scrollViewDidScrollToTop]"];
}

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView {
    [self logMessage:@"[scrollViewWillBeginDecelerating]"];
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    [self logMessage:@"[scrollViewDidEndDecelerating]"];
}

- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
    [self logMessage:@"[scrollViewDidEndScrollingAnimation]"];
}

// Helper method to log messages
- (void)logMessage:(NSString *)message {
    [self.logMessages addObject:message];
    if (self.logMessages.count > 20) {
        [self.logMessages removeObjectAtIndex:0];
    }
    NSString *logText = [self.logMessages componentsJoinedByString:@"\n"];
    self.logTextView.text = logText;
}

// Setup buttons
- (void)setupButtons {
    CGFloat buttonWidth = self.view.frame.size.width / 2;
    CGFloat buttonHeight = 50;
    
    self.backToTopAnimatedButton = [self createButtonWithTitle:@"Back to Top (Animated)" action:@selector(backToTopAnimated)];
    self.backToTopAnimatedButton.frame = CGRectMake(0, self.view.frame.size.height - 100, buttonWidth, buttonHeight);
    [self.view addSubview:self.backToTopAnimatedButton];
    
    self.backToTopNoAnimationButton = [self createButtonWithTitle:@"Back to Top (No Animation)" action:@selector(backToTopNoAnimation)];
    self.backToTopNoAnimationButton.frame = CGRectMake(buttonWidth, self.view.frame.size.height - 100, buttonWidth, buttonHeight);
    [self.view addSubview:self.backToTopNoAnimationButton];
    
    self.scrollToRandomOffsetAnimatedButton = [self createButtonWithTitle:@"Random Offset (Animated)" action:@selector(scrollToRandomOffsetAnimated)];
    self.scrollToRandomOffsetAnimatedButton.frame = CGRectMake(0, self.view.frame.size.height - 50, buttonWidth, buttonHeight);
    [self.view addSubview:self.scrollToRandomOffsetAnimatedButton];
    
    self.scrollToRandomOffsetNoAnimationButton = [self createButtonWithTitle:@"Random Offset (No Animation)" action:@selector(scrollToRandomOffsetNoAnimation)];
    self.scrollToRandomOffsetNoAnimationButton.frame = CGRectMake(buttonWidth, self.view.frame.size.height - 50, buttonWidth, buttonHeight);
    [self.view addSubview:self.scrollToRandomOffsetNoAnimationButton];
}

// Create a button with title and action
- (UIButton *)createButtonWithTitle:(NSString *)title action:(SEL)action {
    UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
    [button setTitle:title forState:UIControlStateNormal];
    [button addTarget:self action:action forControlEvents:UIControlEventTouchUpInside];
    button.backgroundColor = [UIColor lightGrayColor];
    [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    return button;
}

// Button Actions
- (void)backToTopAnimated {
    [self.collectionView setContentOffset:CGPointZero animated:YES];
    [self logMessage:@"Back to top with animation"];
}

- (void)backToTopNoAnimation {
    [self.collectionView setContentOffset:CGPointZero animated:NO];
    [self logMessage:@"Back to top without animation"];
}

- (void)scrollToRandomOffsetAnimated {
    CGFloat maxOffsetY = self.collectionView.contentSize.height - self.collectionView.frame.size.height;
    CGFloat randomOffsetY = arc4random_uniform((unsigned int)maxOffsetY);
    [self.collectionView setContentOffset:CGPointMake(0, randomOffsetY) animated:YES];
    [self logMessage:[NSString stringWithFormat:@"Scrolled to random offset (%.2f) with animation", randomOffsetY]];
}

- (void)scrollToRandomOffsetNoAnimation {
    CGFloat maxOffsetY = self.collectionView.contentSize.height - self.collectionView.frame.size.height;
    CGFloat randomOffsetY = arc4random_uniform((unsigned int)maxOffsetY);
    [self.collectionView setContentOffset:CGPointMake(0, randomOffsetY) animated:NO];
    [self logMessage:[NSString stringWithFormat:@"Scrolled to random offset (%.2f) without animation", randomOffsetY]];
}

@end

Leave a Reply

Your email address will not be published. Required fields are marked *