最近项目需要添加轮播图,想着参考一下别人的源码再封装,然后就找了SDCycleScrollView,然后记录下。
SDCycleScrollView
需要展示图片所以依赖了SDWebImage
框架,先看个例子:
SDCycleScrollView *cycleView = [SDCycleScrollView cycleScrollViewWithFrame:self.view.bounds delegate:self placeholderImage:nil];
cycleView.imageURLStringsGroup = @[@"路径"];
[self.view addSubview:cycleView];
源码
- 我们直接看它的属性就知道是怎么实现的了:
@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
- 初始化:
@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;
}
- 图片展示时使用
SDWebImage
:
- 然后通过
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];
}
- 当设置图片数组时,在内部开启定时器,显示页数:
- (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;
}
}
- 此时子视图要注意适配:
- (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;
}
}
- 交互通过协议回调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]);
}
}
- 最后注意设置
pageControl.userInteractionEnabled = NO
和一些地方:
//解决当父View释放时,当前视图因为被Timer强引用而不能释放的问题
- (void)willMoveToSuperview:(UIView *)newSuperview
{
if (!newSuperview) {
[self invalidateTimer];
}
}
//解决当timer释放后 回调scrollViewDidScroll时访问野指针导致崩溃
- (void)dealloc {
_mainView.delegate = nil;
_mainView.dataSource = nil;
}
- 总结
使用
UICollectionView
封装主要因为重用机制,提升性能。