iOS开发

SDCycleScrollView源码浅析

2021-10-13  本文已影响0人  无悔zero

最近项目需要添加轮播图,想着参考一下别人的源码再封装,然后就找了SDCycleScrollView,然后记录下。

SDCycleScrollView需要展示图片所以依赖了SDWebImage框架,先看个例子:

SDCycleScrollView *cycleView = [SDCycleScrollView cycleScrollViewWithFrame:self.view.bounds delegate:self placeholderImage:nil];
cycleView.imageURLStringsGroup = @[@"路径"];
[self.view addSubview:cycleView];
源码
  1. 我们直接看它的属性就知道是怎么实现的了:
@interface SDCycleScrollView () <UICollectionViewDataSource, UICollectionViewDelegate>

@property (nonatomic, weak) UICollectionView *mainView; // 显示图片的collectionView
@property (nonatomic, weak) UICollectionViewFlowLayout *flowLayout;
@property (nonatomic, strong) NSArray *imagePathsGroup;//图片组
@property (nonatomic, weak) NSTimer *timer;//定时器
@property (nonatomic, assign) NSInteger totalItemsCount;
@property (nonatomic, weak) UIControl *pageControl;//页数显示
@property (nonatomic, strong) UIImageView *backgroundImageView; // 当imageURLs为空时的背景图

@end
  1. 初始化:
@implementation SDCycleScrollView

- (instancetype)initWithFrame:(CGRect)frame
{
    if (self = [super initWithFrame:frame]) {
        [self initialization];//初始化默认值
        [self setupMainView];//设置UICollectionView并添加
    }
    return self;
}

- (void)setupMainView
{
    UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
    flowLayout.minimumLineSpacing = 0;
    flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
    _flowLayout = flowLayout;
    
    UICollectionView *mainView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:flowLayout];
    ...//忽略属性设置
    [self addSubview:mainView];
    _mainView = mainView;
}
  1. 图片展示时使用SDWebImage
  1. 然后通过NSTimer进行轮播:
- (void)setupTimer
{
    [self invalidateTimer]; // 创建定时器前先停止定时器,不然会出现僵尸定时器,导致轮播频率错误
    
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.autoScrollTimeInterval target:self selector:@selector(automaticScroll) userInfo:nil repeats:YES];
    _timer = timer;
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

- (void)invalidateTimer
{
    [_timer invalidate];
    _timer = nil;
}

- (void)automaticScroll
{
    if (0 == _totalItemsCount) return;
    int currentIndex = [self currentIndex];
    int targetIndex = currentIndex + 1;
    [self scrollToIndex:targetIndex];
}

- (void)scrollToIndex:(int)targetIndex
{
    if (targetIndex >= _totalItemsCount) {
        if (self.infiniteLoop) {
            targetIndex = _totalItemsCount * 0.5;
            [_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
        }
        return;
    }
    [_mainView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:targetIndex inSection:0] atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
}
  1. 当设置图片数组时,在内部开启定时器,显示页数:
- (void)setImageURLStringsGroup:(NSArray *)imageURLStringsGroup
{
    ...
    self.imagePathsGroup = [temp copy];
}

- (void)setImagePathsGroup:(NSArray *)imagePathsGroup
{
    ...
    if (imagePathsGroup.count > 1) { // 由于 !=1 包含count == 0等情况
        self.mainView.scrollEnabled = YES;
        [self setAutoScroll:self.autoScroll];//开启轮播
    } else {
        self.mainView.scrollEnabled = NO;
        [self invalidateTimer];//停止定时器
    }
    
    [self setupPageControl];//设置显示页数
    [self.mainView reloadData];//刷新列表
}

-(void)setAutoScroll:(BOOL)autoScroll{
    _autoScroll = autoScroll;
    
    [self invalidateTimer];
    
    if (_autoScroll) {
        [self setupTimer];//开启定时器
    }
}

- (void)setupPageControl
{
    ...
    switch (self.pageControlStyle) {
        case SDCycleScrollViewPageContolStyleAnimated:
        {
            TAPageControl *pageControl = [[TAPageControl alloc] init];
            ...
            [self addSubview:pageControl];
            _pageControl = pageControl;
        }
            break;
        case SDCycleScrollViewPageContolStyleClassic:
        {
            UIPageControl *pageControl = [[UIPageControl alloc] init];
            ...
            [self addSubview:pageControl];
            _pageControl = pageControl;
        }
            break;
        default:
            break;
    }
    ...
}
#pragma mark - UIScrollViewDelegate 滚动时更新显示页数
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    ...
    if ([self.pageControl isKindOfClass:[TAPageControl class]]) {
        TAPageControl *pageControl = (TAPageControl *)_pageControl;
        pageControl.currentPage = indexOnPageControl;
    } else {
        UIPageControl *pageControl = (UIPageControl *)_pageControl;
        pageControl.currentPage = indexOnPageControl;
    }
}
  1. 此时子视图要注意适配:
- (void)layoutSubviews
{
    ...
    _flowLayout.itemSize = self.frame.size;
    
    _mainView.frame = self.bounds;
    ...
    self.pageControl.frame = pageControlFrame;
    self.pageControl.hidden = !_showPageControl;
    
    if (self.backgroundImageView) {
        self.backgroundImageView.frame = self.bounds;
    }
}
  1. 交互通过协议回调cell的点击:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    if ([self.delegate respondsToSelector:@selector(cycleScrollView:didSelectItemAtIndex:)]) {
        [self.delegate cycleScrollView:self didSelectItemAtIndex:[self pageControlIndexWithCurrentCellIndex:indexPath.item]];
    }
    if (self.clickItemOperationBlock) {
        self.clickItemOperationBlock([self pageControlIndexWithCurrentCellIndex:indexPath.item]);
    }
}
  1. 最后注意设置pageControl.userInteractionEnabled = NO和一些地方:
//解决当父View释放时,当前视图因为被Timer强引用而不能释放的问题
- (void)willMoveToSuperview:(UIView *)newSuperview
{
    if (!newSuperview) {
        [self invalidateTimer];
    }
}

//解决当timer释放后 回调scrollViewDidScroll时访问野指针导致崩溃
- (void)dealloc {
    _mainView.delegate = nil;
    _mainView.dataSource = nil;
}

使用UICollectionView封装主要因为重用机制,提升性能。

上一篇下一篇

猜你喜欢

热点阅读