Files
midi_ios/QXLive/HomePage(声播)/View/首页/GKCycleScrollView.m
2025-08-14 10:07:49 +08:00

864 lines
33 KiB
Objective-C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// GKCycleScrollView.m
// GKCycleScrollViewDemo
//
// Created by QuintGao on 2019/9/15.
// Copyright © 2019 QuintGao. All rights reserved.
//
#import "GKCycleScrollView.h"
@interface GKCycleScrollView ()<UIScrollViewDelegate>
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) GKCycleScrollViewCell *currentCell;
@property (nonatomic, assign) NSInteger currentSelectIndex;
// 实际的个数
@property (nonatomic, assign) NSInteger realCount;
// 显示的个数
@property (nonatomic, assign) NSInteger showCount;
// 定时器
@property (nonatomic, weak) NSTimer *timer;
@property (nonatomic, assign) NSInteger timerIndex;
// 当前显示的cell大小
@property (nonatomic, assign) CGSize cellSize;
@property (nonatomic, strong) NSMutableArray *visibleCells;
@property (nonatomic, strong) NSMutableArray *reusableCells;
@property (nonatomic, assign) NSRange visibleRange;
// 处理xib加载时导致的尺寸不准确问题
@property (nonatomic, assign) CGSize originSize;
@end
@implementation GKCycleScrollView
#pragma mark - Life Cycle
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self initialization];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self initialization];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
if (CGSizeEqualToSize(self.originSize, CGSizeZero)) return;
[self updateScrollViewAndCellSize];
}
// 解决当父视图释放时当前视图因为NSTimer强引用而导致的不能释放
- (void)willMoveToSuperview:(UIView *)newSuperview {
if (!newSuperview) {
[self stopTimer];
}
}
- (void)dealloc {
[self stopTimer];
self.scrollView.delegate = nil;
}
// 重新此方法是为了解决当cell超出UIScrollView时不能点击的问题
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
if ([self pointInside:point withEvent:event]) {
// 判断点击的点是否在cell上
for (UIView *cell in self.scrollView.subviews) {
// 将cell的frame转换到当前视图上
CGRect convertFrame = CGRectZero;
convertFrame.size = cell.frame.size;
if (self.direction == GKCycleScrollViewScrollDirectionHorizontal) {
convertFrame.origin.x = cell.frame.origin.x + self.scrollView.frame.origin.x - self.scrollView.contentOffset.x;
convertFrame.origin.y = self.scrollView.frame.origin.y + cell.frame.origin.y;
}else {
convertFrame.origin.x = self.scrollView.frame.origin.x + cell.frame.origin.x;
convertFrame.origin.y = cell.frame.origin.y + self.scrollView.frame.origin.y - self.scrollView.contentOffset.y;
}
// 判断点击的点是否在cell上
if (CGRectContainsPoint(convertFrame, point)) {
// 修复cell上添加其他点击事件无效的bug
UIView *view = [super hitTest:point withEvent:event];
if (view == self || view == cell || view == self.scrollView) return cell;
return view;
}
}
// 判断点击的点是否在UIScrollView上
CGPoint newPoint = CGPointZero;
newPoint.x = point.x - self.scrollView.frame.origin.x + self.scrollView.contentOffset.x;
newPoint.y = point.y - self.scrollView.frame.origin.y + self.scrollView.contentOffset.y;
if ([self.scrollView pointInside:newPoint withEvent:event]) {
return [self.scrollView hitTest:newPoint withEvent:event];
}
// 系统处理
return [super hitTest:point withEvent:event];
}
return nil;
}
#pragma mark - Public Methods
- (void)reloadData {
// 移除所有cell
[self.scrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
// 停止定时器
[self stopTimer];
// 加载数据
if (self.dataSource && [self.dataSource respondsToSelector:@selector(numberOfCellsInCycleScrollView:)]) {
// 实际个数
self.realCount = [self.dataSource numberOfCellsInCycleScrollView:self];
// 展示个数
if (self.isInfiniteLoop) {
self.showCount = self.realCount == 1 ? 1 : self.realCount * 3;
}else {
self.showCount = self.realCount;
}
// 如果总页数为0return
if (self.showCount == 0) return;
if (self.pageControl && [self.pageControl respondsToSelector:@selector(setNumberOfPages:)]) {
[self.pageControl setNumberOfPages:self.realCount];
}
}
// 清除原始数据
[self.visibleCells removeAllObjects];
[self.reusableCells removeAllObjects];
self.visibleRange = NSMakeRange(0, 0);
//cell数量为1或defaultSelectIndex超过了当前数量
if (self.defaultSelectIndex >= self.realCount) {
self.defaultSelectIndex = 0;
}
if (self.realCount == 1) {
self.timerIndex = 0;
}
for (NSInteger i = 0; i < self.showCount; i++){
[self.visibleCells addObject:[NSNull null]];
}
__weak __typeof(self) weakSelf = self;
[self refreshSizeCompletion:^{
[weakSelf initialScrollViewAndCellSize];
}];
}
- (void)refreshSizeCompletion:(void(^)(void))completion {
if (self.bounds.size.width == 0 || self.bounds.size.height == 0) {
[self layoutIfNeeded];
// 此处做延时处理是为了解决使用Masonry布局时导致的view的大小不能及时更新的bug
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (self.bounds.size.width == 0 || self.bounds.size.height == 0) {
[self refreshSizeCompletion:completion];
}else {
!completion ? : completion();
}
});
}else {
!completion ? : completion();
}
}
- (GKCycleScrollViewCell *)dequeueReusableCell{
GKCycleScrollViewCell *cell = self.reusableCells.lastObject;
if (cell) {
[self.reusableCells removeLastObject];
}
return cell;
}
- (void)scrollToCellAtIndex:(NSInteger)index animated:(BOOL)animated {
if (index < self.realCount) {
[self stopTimer];
if (self.isInfiniteLoop) {
self.timerIndex = self.realCount + index;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startTimer) object:nil];
[self performSelector:@selector(startTimer) withObject:nil afterDelay:0.5];
} else {
self.timerIndex = index;
}
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal:
[self.scrollView setContentOffset:CGPointMake(self.cellSize.width * self.timerIndex, 0) animated:animated];
break;
case GKCycleScrollViewScrollDirectionVertical:
[self.scrollView setContentOffset:CGPointMake(0, self.cellSize.height * self.timerIndex) animated:animated];
break;
default:
break;
}
[self setupCellsWithContentOffset:self.scrollView.contentOffset];
[self updateVisibleCellAppearance];
}
}
- (void)adjustCurrentCell {
if (self.isAutoScroll && self.realCount > 0) {
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal: {
self.scrollView.contentOffset = CGPointMake(self.cellSize.width * self.timerIndex, 0);
}
break;
case GKCycleScrollViewScrollDirectionVertical: {
self.scrollView.contentOffset = CGPointMake(0, self.cellSize.height * self.timerIndex);
}
break;
default:
break;
}
}
}
- (void)startTimer {
if (self.realCount > 1 && self.isAutoScroll) {
[self stopTimer];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.autoScrollTime target:self selector:@selector(timerUpdate) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
self.timer = timer;
}
}
- (void)stopTimer {
if (self.timer) {
[self.timer invalidate];
self.timer = nil;
}
}
#pragma mark Private Methods
- (void)initialization {
// 初始化默认数据
self.clipsToBounds = YES;
self.isChangeAlpha = YES;
self.isAutoScroll = YES;
self.isInfiniteLoop = YES;
self.minimumCellAlpha = 1.0f;
self.autoScrollTime = 3.0f;
// 添加scrollView
[self addSubview:self.scrollView];
}
- (void)initialScrollViewAndCellSize {
self.originSize = self.bounds.size;
[self updateScrollViewAndCellSize];
// 默认选中
if (self.defaultSelectIndex >= 0 && self.defaultSelectIndex < self.realCount) {
[self handleCellScrollWithIndex:self.defaultSelectIndex];
}
}
- (void)updateScrollViewAndCellSize {
if (self.bounds.size.width <= 0 || self.bounds.size.height <= 0) return;
// 设置cell尺寸
self.cellSize = CGSizeMake(self.bounds.size.width - 2 * self.leftRightMargin, self.bounds.size.height - 2 * self.topBottomMargin);
if (self.delegate && [self.delegate respondsToSelector:@selector(sizeForCellInCycleScrollView:)]) {
self.cellSize = [self.delegate sizeForCellInCycleScrollView:self];
}
// 设置scrollView大小
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal: {
self.scrollView.frame = CGRectMake(0, 0, self.cellSize.width, self.cellSize.height);
self.scrollView.contentSize = CGSizeMake(self.cellSize.width * self.showCount,0);
self.scrollView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
if (self.realCount > 1) {
CGPoint offset = CGPointZero;
if (self.isInfiniteLoop) { // 开启无限轮播
// 滚动到第二组
offset = CGPointMake(self.cellSize.width * (self.realCount + self.defaultSelectIndex), 0);
self.timerIndex = self.realCount + self.defaultSelectIndex;
}else {
offset = CGPointMake(self.cellSize.width * self.defaultSelectIndex, 0);
self.timerIndex = self.defaultSelectIndex;
}
[self.scrollView setContentOffset:offset animated:NO];
// 自动轮播
if (self.isAutoScroll) {
[self startTimer];
}
}
}
break;
case GKCycleScrollViewScrollDirectionVertical: {
self.scrollView.frame = CGRectMake(0, 0, self.cellSize.width, self.cellSize.height);
self.scrollView.contentSize = CGSizeMake(0, self.cellSize.height * self.showCount);
self.scrollView.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
if (self.realCount > 1) {
CGPoint offset = CGPointZero;
if (self.isInfiniteLoop) { // 开启无限轮播
// 滚动到第二组
offset = CGPointMake(0, self.cellSize.height * (self.realCount + self.defaultSelectIndex));
self.timerIndex = self.realCount + self.defaultSelectIndex;
}else {
offset = CGPointMake(0, self.cellSize.height * self.defaultSelectIndex);
self.timerIndex = self.defaultSelectIndex;
}
[self.scrollView setContentOffset:offset animated:NO];
// 自动轮播
if (self.isAutoScroll) {
[self startTimer];
}
}
}
break;
default:
break;
}
// 根据当前scrollView的offset设置显示的cell
[self setupCellsWithContentOffset:self.scrollView.contentOffset];
// 更新可见cell的显示
[self updateVisibleCellAppearance];
}
- (void)setupCellsWithContentOffset:(CGPoint)offset {
if (self.showCount == 0) return;
if (self.cellSize.width <= 0 || self.cellSize.height <= 0) return;
//计算_visibleRange
CGFloat originX = self.scrollView.frame.origin.x == 0 ? 0.01 : self.scrollView.frame.origin.x;
CGFloat originY = self.scrollView.frame.origin.y == 0 ? 0.01 : self.scrollView.frame.origin.y;
CGPoint startPoint = CGPointMake(offset.x - originX, offset.y - originY);
CGPoint endPoint = CGPointMake(offset.x + self.scrollView.frame.size.width + originX, offset.y + self.scrollView.frame.size.height + originY);
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal: {
NSInteger startIndex = 0;
for (NSInteger i = 0; i < self.visibleCells.count; i++) {
if (self.cellSize.width * (i + 1) > startPoint.x) {
startIndex = i;
break;
}
}
NSInteger endIndex = startIndex;
for (NSInteger i = startIndex; i < self.visibleCells.count; i++) {
//如果都不超过则取最后一个
if ((self.cellSize.width * (i + 1) < endPoint.x && self.cellSize.width * (i + 2) >= endPoint.x) || i + 2 == self.visibleCells.count) {
endIndex = i + 1;
break;
}
}
// 可见页分别向前向后扩展一个,提高效率
startIndex = MAX(startIndex, 0);
endIndex = MIN(endIndex, self.visibleCells.count - 1);
self.visibleRange = NSMakeRange(startIndex, endIndex - startIndex + 1);
for (NSInteger i = startIndex; i <= endIndex; i++) {
[self addCellAtIndex:i];
}
for (NSInteger i = 0; i < startIndex; i ++) {
[self removeCellAtIndex:i];
}
for (NSInteger i = endIndex + 1; i < self.visibleCells.count; i ++) {
[self removeCellAtIndex:i];
}
}
break;
case GKCycleScrollViewScrollDirectionVertical: {
NSInteger startIndex = 0;
for (NSInteger i = 0; i < self.visibleCells.count; i++) {
if (self.cellSize.height * (i +1) > startPoint.y) {
startIndex = i;
break;
}
}
NSInteger endIndex = startIndex;
for (NSInteger i = startIndex; i < self.visibleCells.count; i++) {
//如果都不超过则取最后一个
if ((self.cellSize.height * (i + 1) < endPoint.y && self.cellSize.height * (i + 2) >= endPoint.y) || i+ 2 == self.visibleCells.count) {
endIndex = i + 1;//i+2 是以个数所以其index需要减去1
break;
}
}
//可见页分别向前向后扩展一个,提高效率
startIndex = MAX(startIndex - 1, 0);
endIndex = MIN(endIndex + 1, self.visibleCells.count - 1);
self.visibleRange = NSMakeRange(startIndex, endIndex - startIndex + 1);
for (NSInteger i = startIndex; i <= endIndex; i++) {
[self addCellAtIndex:i];
}
for (NSInteger i = 0; i < startIndex; i ++) {
[self removeCellAtIndex:i];
}
for (NSInteger i = endIndex + 1; i < self.visibleCells.count; i ++) {
[self removeCellAtIndex:i];
}
break;
}
default:
break;
}
}
- (void)updateVisibleCellAppearance {
if (self.showCount == 0) return;
if (self.cellSize.width <= 0 || self.cellSize.height <= 0) return;
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal:{
CGFloat offsetX = self.scrollView.contentOffset.x;
for (NSInteger i = self.visibleRange.location; i < NSMaxRange(self.visibleRange); i++) {
GKCycleScrollViewCell *cell = self.visibleCells[i];
if ((NSObject *)cell == [NSNull null]) continue;
CGFloat originX = cell.frame.origin.x;
CGFloat delta = fabs(originX - offsetX);
CGRect originCellFrame = (CGRect){{self.cellSize.width * i, 0}, self.cellSize};
CGFloat leftRightInset = 0;
CGFloat topBottomInset = 0;
CGFloat alpha = 0;
if (delta < self.cellSize.width) {
alpha = (delta / self.cellSize.width) * self.minimumCellAlpha;
CGFloat adjustLeftRightMargin = self.leftRightMargin == 0 ? 0 : self.leftRightMargin + 1;
CGFloat adjustTopBottomMargin = self.topBottomMargin == 0 ? 0 : self.topBottomMargin;
leftRightInset = adjustLeftRightMargin * delta / self.cellSize.width;
topBottomInset = adjustTopBottomMargin * delta / self.cellSize.width;
NSInteger index = self.realCount == 0 ? 0 : i % self.realCount;
if (index == self.currentSelectIndex) {
[self.scrollView bringSubviewToFront:cell];
}
} else {
alpha = self.minimumCellAlpha;
leftRightInset = self.leftRightMargin;
topBottomInset = self.topBottomMargin;
[self.scrollView sendSubviewToBack:cell];
}
if (self.leftRightMargin == 0 && self.topBottomMargin == 0) {
cell.frame = originCellFrame;
}else {
CGFloat scaleX = (self.cellSize.width - leftRightInset * 2) / self.cellSize.width;
CGFloat scaleY = (self.cellSize.height - topBottomInset * 2) / self.cellSize.height;
UIEdgeInsets insets = UIEdgeInsetsMake(topBottomInset, leftRightInset - 0.1, topBottomInset, leftRightInset);
cell.layer.transform = CATransform3DMakeScale(scaleX, scaleY, 1.0);
cell.frame = UIEdgeInsetsInsetRect(originCellFrame, insets);
}
// 获取当前cell
if (cell.tag == self.currentSelectIndex) {
self.currentCell = cell;
}
// 透明度渐变
if (self.isChangeAlpha) {
cell.coverView.alpha = alpha;
}
}
}
break;
case GKCycleScrollViewScrollDirectionVertical:{
CGFloat offsetY = self.scrollView.contentOffset.y;
for (NSInteger i = self.visibleRange.location; i < NSMaxRange(self.visibleRange); i++) {
GKCycleScrollViewCell *cell = self.visibleCells[i];
if ((NSObject *)cell == [NSNull null]) continue;
CGFloat originY = cell.frame.origin.y;
CGFloat delta = fabs(originY - offsetY);
CGRect originCellFrame = (CGRect){{0, self.cellSize.height * i}, self.cellSize};
CGFloat leftRightInset = 0;
CGFloat topBottomInset = 0;
CGFloat alpha = 0;
if (delta < self.cellSize.height) {
alpha = (delta / self.cellSize.height) * self.minimumCellAlpha;
CGFloat adjustLeftRightMargin = self.leftRightMargin == 0 ? 0 : self.leftRightMargin;
CGFloat adjustTopBottomMargin = self.topBottomMargin == 0 ? 0 : self.topBottomMargin + 1;
leftRightInset = adjustLeftRightMargin * delta / self.cellSize.height;
topBottomInset = adjustTopBottomMargin * delta / self.cellSize.height;
NSInteger index = self.realCount == 0 ? 0 : i % self.realCount;
if (index == self.currentSelectIndex) {
[self.scrollView bringSubviewToFront:cell];
}
} else {
alpha = self.minimumCellAlpha;
leftRightInset = self.leftRightMargin;
topBottomInset = self.topBottomMargin;
[self.scrollView sendSubviewToBack:cell];
}
if (self.leftRightMargin == 0 && self.topBottomMargin == 0) {
cell.frame = originCellFrame;
}else {
CGFloat scaleX = (self.cellSize.width - leftRightInset * 2) / self.cellSize.width;
CGFloat scaleY = (self.cellSize.height - topBottomInset * 2) / self.cellSize.height;
UIEdgeInsets insets = UIEdgeInsetsMake(topBottomInset - 0.1, leftRightInset, topBottomInset, leftRightInset);
cell.layer.transform = CATransform3DMakeScale(scaleX, scaleY, 1.0f);
cell.frame = UIEdgeInsetsInsetRect(originCellFrame, insets);
}
// 透明度渐变
if (self.isChangeAlpha) {
cell.coverView.alpha = alpha;
}
}
}
break;
default:
break;
}
}
- (void)addCellAtIndex:(NSInteger)index {
NSParameterAssert(index >= 0 && index < self.visibleCells.count);
GKCycleScrollViewCell *cell = self.visibleCells[index];
if ((NSObject *)cell == [NSNull null]) {
cell = [self.dataSource cycleScrollView:self cellForViewAtIndex:index % self.realCount];
if (cell) {
[self.visibleCells replaceObjectAtIndex:index withObject:cell];
cell.tag = index % self.realCount;
[cell setupCellFrame:CGRectMake(0, 0, self.cellSize.width, self.cellSize.height)];
if (!self.isChangeAlpha) cell.coverView.hidden = YES;
__weak __typeof(self) weakSelf = self;
cell.didCellClick = ^(NSInteger index) {
[weakSelf handleCellSelectWithIndex:index];
};
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal:
cell.frame = CGRectMake(self.cellSize.width * index, 0, self.cellSize.width, self.cellSize.height);
break;
case GKCycleScrollViewScrollDirectionVertical:
cell.frame = CGRectMake(0, self.cellSize.height * index, self.cellSize.width, self.cellSize.height);
break;
default:
break;
}
if (!cell.superview) {
[self.scrollView addSubview:cell];
}
}
}
}
- (void)removeCellAtIndex:(NSInteger)index{
GKCycleScrollViewCell *cell = self.visibleCells[index];
if ((NSObject *)cell == [NSNull null]) return;
[self.reusableCells addObject:cell];
if (cell.superview) {
[cell removeFromSuperview];
}
[self.visibleCells replaceObjectAtIndex:index withObject:[NSNull null]];
}
- (void)handleCellSelectWithIndex:(NSInteger)index {
if ([self.delegate respondsToSelector:@selector(cycleScrollView:didSelectCellAtIndex:)]) {
[self.delegate cycleScrollView:self didSelectCellAtIndex:index];
}
}
- (void)handleCellScrollWithIndex:(NSInteger)index {
self.currentSelectIndex = index;
if (self.pageControl && [self.pageControl respondsToSelector:@selector(setCurrentPage:)]) {
self.pageControl.currentPage = index;
}
// 获取当前cell
for (NSInteger i = self.visibleRange.location; i < NSMaxRange(self.visibleRange); i++) {
GKCycleScrollViewCell *cell = self.visibleCells[i];
if ((NSObject *)cell == [NSNull null]) continue;
if (cell.tag == index) {
self.currentCell = cell;
}
}
if ([self.delegate respondsToSelector:@selector(cycleScrollView:didScrollCellToIndex:)]) {
[self.delegate cycleScrollView:self didScrollCellToIndex:index];
}
}
- (void)timerUpdate {
self.timerIndex++;
// bug fixed解决反向滑动停止后可能出现的自动滚动错乱问题
if (self.timerIndex > self.realCount * 2) {
self.timerIndex = self.realCount * 2;
}
if (!self.isInfiniteLoop) {
if (self.timerIndex >= self.realCount) {
self.timerIndex = 0;
}
}
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal: {
[self.scrollView setContentOffset:CGPointMake(self.cellSize.width * self.timerIndex, 0) animated:YES];
}
break;
case GKCycleScrollViewScrollDirectionVertical: {
[self.scrollView setContentOffset:CGPointMake(0, self.cellSize.height * self.timerIndex) animated:YES];
}
break;
default:
break;
}
}
#pragma mark UIScrollView Delegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (self.realCount == 0) return;
if (self.cellSize.width <= 0 || self.cellSize.height <= 0) return;
NSInteger index = 0;
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal: {
index = (NSInteger)round(self.scrollView.contentOffset.x / self.cellSize.width) % self.realCount;
}
break;
case GKCycleScrollViewScrollDirectionVertical: {
index = (NSInteger)round(self.scrollView.contentOffset.y / self.cellSize.height) % self.realCount;
}
break;
default:
break;
}
if (self.isInfiniteLoop) {
if (self.realCount > 1) {
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal: {
CGFloat horIndex = scrollView.contentOffset.x / self.cellSize.width;
if (horIndex >= 2 * self.realCount) {
scrollView.contentOffset = CGPointMake(self.cellSize.width * self.realCount, 0);
self.timerIndex = self.realCount;
}
if (horIndex <= (self.realCount - 1)) {
scrollView.contentOffset = CGPointMake(self.cellSize.width * (2 * self.realCount - 1), 0);
self.timerIndex = 2 * self.realCount - 1;
}
}
break;
case GKCycleScrollViewScrollDirectionVertical: {
NSInteger verIndex = scrollView.contentOffset.y / self.cellSize.height;
if (verIndex >= 2 * self.realCount) {
scrollView.contentOffset = CGPointMake(0, self.cellSize.height * self.realCount);
self.timerIndex = self.realCount;
}
if (verIndex <= (self.realCount - 1)) {
scrollView.contentOffset = CGPointMake(0, self.cellSize.height * (2 * self.realCount - 1));
self.timerIndex = 2 * self.realCount - 1;
}
}
break;
default:
break;
}
}else {
index = 0;
}
}
[self setupCellsWithContentOffset:scrollView.contentOffset];
[self updateVisibleCellAppearance];
if (index >= 0 && self.currentSelectIndex != index) {
[self handleCellScrollWithIndex:index];
}
[self handleScrollViewDidScroll:scrollView];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self stopTimer];
if ([self.delegate respondsToSelector:@selector(cycleScrollView:willBeginDragging:)]) {
[self.delegate cycleScrollView:self willBeginDragging:scrollView];
}
}
// 结束拖拽时调用decelerate是否有减速
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
if (!decelerate) {
[self startTimer];
}
if ([self.delegate respondsToSelector:@selector(cycleScrollView:didEndDragging:willDecelerate:)]) {
[self.delegate cycleScrollView:self didEndDragging:scrollView willDecelerate:decelerate];
}
}
// 结束减速是调用
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
[self startTimer];
if ([self.delegate respondsToSelector:@selector(cycleScrollView:didEndDecelerating:)]) {
[self.delegate cycleScrollView:self didEndDecelerating:scrollView];
}
}
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
if (self.realCount > 1 && self.isAutoScroll) {
switch (self.direction) {
case GKCycleScrollViewScrollDirectionHorizontal: {
NSInteger index = round(targetContentOffset->x / self.cellSize.width);
self.timerIndex = index;
}
break;
case GKCycleScrollViewScrollDirectionVertical: {
NSInteger index = round(targetContentOffset->y / self.cellSize.height);
self.timerIndex = index;
}
break;
default:
break;
}
}
}
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
[self updateVisibleCellAppearance];
if ([self.delegate respondsToSelector:@selector(cycleScrollView:didEndScrollingAnimation:)]) {
[self.delegate cycleScrollView:self didEndScrollingAnimation:scrollView];
}
}
- (void)handleScrollViewDidScroll:(UIScrollView *)scrollView {
if ([self.delegate respondsToSelector:@selector(cycleScrollView:didScroll:)]) {
[self.delegate cycleScrollView:self didScroll:scrollView];
}
if ([self.delegate respondsToSelector:@selector(cycleScrollView:scrollingFromIndex:toIndex:ratio:)]) {
BOOL isFirstRevirse = NO; // 是否在第一个位置反向滑动
CGFloat ratio = 0; // 滑动百分比
if (self.direction == GKCycleScrollViewScrollDirectionHorizontal) {
CGFloat offsetX = scrollView.contentOffset.x;
CGFloat maxW = self.realCount * scrollView.bounds.size.width;
CGFloat changeOffsetX = self.isInfiniteLoop ? (offsetX - maxW) : offsetX;
if (changeOffsetX < 0) {
changeOffsetX = -changeOffsetX;
isFirstRevirse = YES;
}
ratio = (changeOffsetX / scrollView.bounds.size.width);
}else if (self.direction == GKCycleScrollViewScrollDirectionVertical) {
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat maxW = self.realCount * scrollView.bounds.size.height;
CGFloat changeOffsetY = self.isInfiniteLoop ? (offsetY - maxW) : offsetY;
if (changeOffsetY < 0) {
changeOffsetY = -changeOffsetY;
isFirstRevirse = YES;
}
ratio = (changeOffsetY / scrollView.bounds.size.height);
}
if (ratio > self.realCount || ratio < 0) return; // 越界,不作处理
ratio = MAX(0, MIN(self.realCount, ratio));
NSInteger baseIndex = floor(ratio);
if (baseIndex + 1 > self.realCount) {
// 右边越界了
baseIndex = 0;
}
CGFloat remainderRatio = ratio - baseIndex;
if (remainderRatio <= 0 || remainderRatio >= 1) return;
NSInteger toIndex = 0;
if (isFirstRevirse) {
baseIndex = self.realCount - 1;
toIndex = 0;
remainderRatio = 1 - remainderRatio;
}else if (baseIndex == self.realCount - 1) {
toIndex = 0;
}else {
toIndex = baseIndex + 1;
}
[self.delegate cycleScrollView:self scrollingFromIndex:baseIndex toIndex:toIndex ratio:remainderRatio];
}
}
#pragma mark - 懒加载
- (UIScrollView *)scrollView {
if (!_scrollView) {
_scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
_scrollView.scrollsToTop = NO;
_scrollView.delegate = self;
_scrollView.pagingEnabled = YES;
_scrollView.clipsToBounds = NO;
_scrollView.showsHorizontalScrollIndicator = NO;
_scrollView.showsVerticalScrollIndicator = NO;
}
return _scrollView;
}
- (NSMutableArray *)visibleCells {
if (!_visibleCells) {
_visibleCells = [NSMutableArray new];
}
return _visibleCells;
}
- (NSMutableArray *)reusableCells {
if (!_reusableCells) {
_reusableCells = [NSMutableArray new];
}
return _reusableCells;
}
@end