昨天将以前写的一个循环滚动视图重新整理了一下。以后还会继续完善这个demo,github托管地址:
https://github.com/JerryLMJ/LMJEndlessLoopScrollView
如果此demo帮助到你,请赐给一颗star,你的鼓励是我coding的动力
主要讲一下核心的代码什么的,具体的下载下来自己来看吧。
原理
主要是利用UIScrollView
实现,设置scrollVIew
的contentSize
为三倍控件宽度,不断重置添加在scrollView
上的三个内容子视图,从而实现无限循环。
用到的类别
先说一下用到的两个自定义类别:
- NSTimer (ForLMJEndlessLoopScrollView)
// 暂停
- (void)pause;
// 重新开始
- (void)restart;
// 延迟一定时间启动
- (void)restartAfterTimeInterval:(NSTimeInterval)interval;
- (void)pause{
if ([self isValid]) {
[self setFireDate:[NSDate distantFuture]];
}
}
- (void)restart{
if ([self isValid]) {
[self setFireDate:[NSDate date]];
}
}
- (void)restartAfterTimeInterval:(NSTimeInterval)interval{
if ([self isValid]) {
[self setFireDate:[NSDate dateWithTimeIntervalSinceNow:interval]];
}
}
- UIView (ForLMJEndlessLoopScrollView)
- (UIView *)copyView; // 复制一个相同的UIView
- (UIView *)copyView{
NSData * tempArchive = [NSKeyedArchiver archivedDataWithRootObject:self];
return [NSKeyedUnarchiver unarchiveObjectWithData:tempArchive];
}
代理
@protocol LMJEndlessLoopScrollViewDelegate <NSObject>
@required
// 要添加的内容子视图的个数
- (NSInteger)numberOfContentViewsInLoopScrollView:(LMJEndlessLoopScrollView *)loopScrollView;
// 每一个序号位置对应的内容子视图(这个代理会调用很多次)
- (UIView *)loopScrollView:(LMJEndlessLoopScrollView *)loopScrollView contentViewAtIndex:(NSInteger)index;
@optional
// 滚动到当前内容子视图的序号
- (void)loopScrollView:(LMJEndlessLoopScrollView *)loopScrollView currentContentViewAtIndex:(NSInteger)index;
// 点击了某个内容子视图
- (void)loopScrollView:(LMJEndlessLoopScrollView *)loopScrollView didSelectContentViewAtIndex:(NSInteger)index;
@end
核心代码
.h文件
@interface LMJEndlessLoopScrollView : UIView
@property (nonatomic,assign) id<LMJEndlessLoopScrollViewDelegate> delegate;
// 当duration<=0时,默认不自动滚动
- (id)initWithFrame:(CGRect)frame animationScrollDuration:(NSTimeInterval)duration;
- (void)reloadData;
@end
.m文件
#define SelfWidth_LMJ (self.frame.size.width)
#define SelfHeight_LMJ (self.frame.size.height)
@interface LMJEndlessLoopScrollView () <UIScrollViewDelegate>
{
UIScrollView * _scrollView;
NSInteger _totalPageCount;
NSInteger _currentPageIndex;
NSTimer * _animationTimer;
NSTimeInterval _animationDuration;
}
@end
@implementation LMJEndlessLoopScrollView
- (id)initWithFrame:(CGRect)frame animationScrollDuration:(NSTimeInterval)duration{
self = [super initWithFrame:frame];
if (self) {
[self initData];
[self initAnimationScrollTimerWithDuration:duration];
[self buildScrollView];
}
return self;
}
#pragma mark - Init
- (void)initData{
_animationTimer = nil;
_animationDuration = 0;
}
- (void)initAnimationScrollTimerWithDuration:(NSTimeInterval)duration{
_animationDuration = duration;
if (duration > 0) {
_animationTimer = [NSTimer scheduledTimerWithTimeInterval:duration
target:self
selector:@selector(startScroll:)
userInfo:nil
repeats:YES];
// 将timer加入到currentRunLoop中主要是防止在操作UI的时候影响到timer驱动的自动滚动
NSRunLoop *main = [NSRunLoop currentRunLoop];
[main addTimer:_animationTimer forMode:NSRunLoopCommonModes];
[_animationTimer pause];
}else{
/*
注意: 当 duration <= 0 时,_animationTimer为nil,由于在NSTimer扩展的方法中进行了空对象判断,所以本页代码中都没有进行空判断,也就是说此时对_animationTimer的操作都无效
*/
}
}
- (void)buildScrollView{
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, SelfWidth_LMJ, SelfHeight_LMJ)];
_scrollView.delegate = self;
_scrollView.contentSize = CGSizeMake(SelfWidth_LMJ *3, SelfHeight_LMJ);
_scrollView.contentOffset = CGPointMake(SelfWidth_LMJ, 0);
_scrollView.pagingEnabled = YES;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.showsVerticalScrollIndicator = NO;
[self addSubview:_scrollView];
}
#pragma mark - Set
- (void)setDelegate:(id<LMJEndlessLoopScrollViewDelegate>)delegate{
_delegate = delegate;
/*
为什么要在delegate的set函数中reloadData,主要是因为在reload的过程中需要调用代理获取数据源,所以只有设置了代理之后才可以调用代理函数
*/
[self reloadData];
}
#pragma mark - ReloadData
- (void)reloadData{
_currentPageIndex = 0;
_totalPageCount = 0;
if ([self.delegate respondsToSelector:@selector(numberOfContentViewsInLoopScrollView:)]) {
_totalPageCount = [self.delegate numberOfContentViewsInLoopScrollView:self];
}else{
NSAssert(NO, @"请实现numberOfContentViewsInLoopScrollView:代理函数");
}
[self resetContentViews];
[_animationTimer restartAfterTimeInterval:_animationDuration];
}
#pragma mark - Methods
- (void)resetContentViews{
// 移除scrollView上的所有子视图
[_scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
NSInteger previousPageIndex = [self getPreviousPageIndexWithCurrentPageIndex:_currentPageIndex];
NSInteger currentPageIndex = _currentPageIndex;
NSInteger nextPageIndex = [self getNextPageIndexWithCurrentPageIndex:_currentPageIndex];
UIView * previousContentView;
UIView * currentContentView;
UIView * nextContentView;
if ([self.delegate respondsToSelector:@selector(loopScrollView:contentViewAtIndex:)]) {
previousContentView = [self.delegate loopScrollView:self contentViewAtIndex:previousPageIndex];
currentContentView = [self.delegate loopScrollView:self contentViewAtIndex:currentPageIndex];
nextContentView = [self.delegate loopScrollView:self contentViewAtIndex:nextPageIndex];
NSArray * viewsArr = @[[previousContentView copyView],[currentContentView copyView],[nextContentView copyView]]; // copy操作主要是为了只有两张内容视图的情况
for (int i = 0; i < viewsArr.count; i++) {
UIView * contentView = viewsArr[i];
[contentView setFrame:CGRectMake(SelfWidth_LMJ*i, 0, contentView.frame.size.width, contentView.frame.size.height)];
contentView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapContentView:)];
[contentView addGestureRecognizer:tapGesture];
[_scrollView addSubview:contentView];
}
[_scrollView setContentOffset:CGPointMake(SelfWidth_LMJ, 0)];
}else{
// NSAssert(NO, @"请实现loopScrollView:contentViewAtIndex:代理函数");
}
}
// 获取当前页上一页的序号
- (NSInteger)getPreviousPageIndexWithCurrentPageIndex:(NSInteger)currentIndex{
if (currentIndex == 0) {
return _totalPageCount -1;
}else{
return currentIndex -1;
}
}
// 获取当前页下一页的序号
- (NSInteger)getNextPageIndexWithCurrentPageIndex:(NSInteger)currentIndex{
if (currentIndex == _totalPageCount -1) {
return 0;
}else{
return currentIndex +1;
}
}
#pragma mark - UIScrollView Delegate
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
// 当手动滑动时 暂停定时器
[_animationTimer pause];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
// 当手动滑动结束时 开启定时器
[_animationTimer restartAfterTimeInterval:_animationDuration];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
int contentOffsetX = scrollView.contentOffset.x;
if(contentOffsetX >= (2 * SelfWidth_LMJ)) {
_currentPageIndex = [self getNextPageIndexWithCurrentPageIndex:_currentPageIndex];
// 调用代理函数 当前页面序号
if ([self.delegate respondsToSelector:@selector(loopScrollView:currentContentViewAtIndex:)]) {
[self.delegate loopScrollView:self currentContentViewAtIndex:_currentPageIndex];
}
[self resetContentViews];
}
if(contentOffsetX <= 0) {
_currentPageIndex = [self getPreviousPageIndexWithCurrentPageIndex:_currentPageIndex];
// 调用代理函数 当前页面序号
if ([self.delegate respondsToSelector:@selector(loopScrollView:currentContentViewAtIndex:)]) {
[self.delegate loopScrollView:self currentContentViewAtIndex:_currentPageIndex];
}
[self resetContentViews];
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
[scrollView setContentOffset:CGPointMake(SelfWidth_LMJ, 0) animated:YES];
}
#pragma mark - Action
- (void)startScroll:(NSTimer *)timer{
// 主要为了处理没有滑完整页的情况
CGFloat contentOffsetX = ( (int)(_scrollView.contentOffset.x +SelfWidth_LMJ) / (int)SelfWidth_LMJ ) * SelfWidth_LMJ;
CGPoint newOffset = CGPointMake(contentOffsetX, 0);
[_scrollView setContentOffset:newOffset animated:YES];
}
#pragma mark - TapAction
- (void)tapContentView:(UITapGestureRecognizer *)gesture{
if ([self.delegate respondsToSelector:@selector(loopScrollView:didSelectContentViewAtIndex:)]) {
[self.delegate loopScrollView:self didSelectContentViewAtIndex:_currentPageIndex];
}
}
使用
#import "LMJEndlessLoopScrollView.h"
@interface ViewController () <LMJEndlessLoopScrollViewDelegate>
{
NSMutableArray * _contentViewsDataArr;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor lightGrayColor];
_contentViewsDataArr = [NSMutableArray array];
NSArray *colorArray = @[[UIColor cyanColor],[UIColor blueColor],[UIColor greenColor],[UIColor yellowColor],[UIColor purpleColor]];
for (int i = 0; i < 2; i++) {
UILabel *tempLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 280, 200)];
tempLabel.backgroundColor = colorArray[i];
tempLabel.textAlignment = NSTextAlignmentCenter;
tempLabel.text = [NSString stringWithFormat:@"%d",i];
tempLabel.font = [UIFont boldSystemFontOfSize:50];
[_contentViewsDataArr addObject:tempLabel];
}
LMJEndlessLoopScrollView * scrollView = [[LMJEndlessLoopScrollView alloc] initWithFrame:CGRectMake(20, 100, 280, 200) animationScrollDuration:3];
scrollView.delegate = self;
scrollView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:scrollView];
}
#pragma mark - LMJEndlessLoopScrollView Delegate
- (NSInteger)numberOfContentViewsInLoopScrollView:(LMJEndlessLoopScrollView *)loopScrollView{
return _contentViewsDataArr.count;
}
- (UIView *)loopScrollView:(LMJEndlessLoopScrollView *)loopScrollView contentViewAtIndex:(NSInteger)index{
return _contentViewsDataArr[index];
}
- (void)loopScrollView:(LMJEndlessLoopScrollView *)loopScrollView didSelectContentViewAtIndex:(NSInteger)index{
NSLog(@"----点击-----%ld",index);
}
- (void)loopScrollView:(LMJEndlessLoopScrollView *)loopScrollView currentContentViewAtIndex:(NSInteger)index{
NSLog(@"----当前-----%ld",index);
}
@end
github托管地址:
https://github.com/JerryLMJ/LMJEndlessLoopScrollView
如果此demo帮助到你,请赐给一颗star,你的鼓励是我coding的动力
版权声明:出自MajorLMJ技术博客的原创作品 ,转载时必须注明出处及相应链接!