iOS 实现UIScrollView的无限轮播(原理)

 

第一种

同学们在写需求的时候肯定会时常用到UIScrollView。而说到UIScrollView,大家最先想到的肯定就是它上面的无限轮播功能。苹果在UIScrollView上并没有提供相应的方法让大家实现轮播,所以就需要通过代码进行处理来实现。先上图

 

无限轮播效果图.gif

我先给大家讲讲其实现的原理:
我们假设用几张图片实现轮播效果。首先,我们需要打开UIScrollView的分页滑动

/// 分页滑动
_scrollView.scrollEnabled = YES;

它方便的帮助我们实现了轮播的效果,然后就需要我们来实现“无限的”轮播。接下来,我们就需要摆放图片了,在摆放图片时需要注意,我们需要在第一张图片的位置摆放最后一张图片(可能有点懵哈,不过不要着急慢慢往下看),然后我们依次摆放图片(从第一张到最后一张),最后在所有图片的尾部我们再放上第一张图片。这样我们就多放了两张图片(分别在首尾多放了一张图)。我把对应的方法写一下:

/// 将图片放置在UIScrollView上
-(void)setupImage {
    /// 在UIScrollView的最前面添加一张图片
    UIImageView *firstImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, self.scrollView.frame.size.height)];
    /// 图片名是最后一张图片
    firstImageView.image = [UIImage imageNamed:self.imageNameList.lastObject];
    [self.scrollView addSubview:firstImageView];
    
    /// 添加图片
    for (NSInteger index = 0; index < self.imageNameList.count; index ++) {
        /// UIScrollView上的每一张图片
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake((index + 1) * kScreenWidth, 0, kScreenWidth, self.scrollView.frame.size.height)];
        imageView.image = [UIImage imageNamed:self.imageNameList[index]];
        
        [self.scrollView addSubview:imageView];
        self.scrollView.contentSize = CGSizeMake((index + 2) * self.scrollView.bounds.size.width, 0);
    }
    
    /// 在UIScrollView的最后面添加一张图片
    UIImageView *lastImageView = [[UIImageView alloc] initWithFrame:CGRectMake((self.imageNameList.count + 1) * kScreenWidth, 0, kScreenWidth, self.scrollView.frame.size.height)];
    /// 图片名是第一张图片
    lastImageView.image = [UIImage imageNamed:self.imageNameList.firstObject];
    [self.scrollView addSubview:lastImageView];
    
    /// 设置UIScrollView的偏移量
    self.scrollView.contentSize = CGSizeMake((self.imageNameList.count + 2) * self.scrollView.bounds.size.width, 0);
    
    /// 设置UIScrollView的起始偏移距离(将第一张图片跳过)
    self.scrollView.contentOffset = CGPointMake(kScreenWidth, 0);
    
    /// 图片总数
    self.pageControl.numberOfPages = self.imageNameList.count;
    self.pageControl.currentPage = 0;
}

其实,如果大家看到这里,应该就会大致明白无线轮播的实现原理了。接下来就是最后一步,在UIScrollView的代理方法里面写逻辑:判断UIScrollView的偏移量,当其滑动到首位时(显示的是最后一张图片),滑动停止,就把偏移量修改最后面图片的位置上(倒数第二张)。同理,当UIScrollView滑动到最后时(显示的是第一张图片),滑动停止,就把偏移量修改到第一张图片的位置上(正数第二张)。

#pragma mark - UIScrollViewDelegate
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    /// 当UIScrollView滑动到第一位停止时,将UIScrollView的偏移位置改变
    if (scrollView.contentOffset.x == 0) {
        scrollView.contentOffset = CGPointMake(self.imageNameList.count * kScreenWidth, 0);
        self.pageControl.currentPage = self.imageNameList.count;
    /// 当UIScrollView滑动到最后一位停止时,将UIScrollView的偏移位置改变
    } else if (scrollView.contentOffset.x == (self.imageNameList.count + 1)* kScreenWidth) {
        scrollView.contentOffset = CGPointMake(kScreenWidth, 0);
        self.pageControl.currentPage = 0;
    } else {
        self.pageControl.currentPage = scrollView.contentOffset.x / kScreenWidth - 1;
    }
}

ok,原理其实就是这样。在首尾多加两张图片当做占位符,然后当UIScrollView滑动到占位符的位置时,改变UIScrollView的偏移量,简单且方便。下面就是全部代码:

#import "ViewController.h"

#define kScreenWidth [UIScreen mainScreen].bounds.size.width

@interface ViewController () <UIScrollViewDelegate>

/// 滑动控制器
@property (nonatomic, strong) UIScrollView *scrollView;
/// 图片数组
@property (nonatomic, strong) NSArray<NSString *> *imageNameList;
/// 页码控制器
@property (nonatomic, strong) UIPageControl *pageControl;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 设置图片名的数组
    self.imageNameList = @[@"image0", @"image1", @"image2", @"image3"];
    
    // 添加图片
    [self setupImage];
}

/// 将图片放置在UIScrollView上
-(void)setupImage {
    /// 在UIScrollView的最前面添加一张图片
    UIImageView *firstImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, self.scrollView.frame.size.height)];
    /// 图片名是最后一张图片
    firstImageView.image = [UIImage imageNamed:self.imageNameList.lastObject];
    [self.scrollView addSubview:firstImageView];
    
    /// 添加图片
    for (NSInteger index = 0; index < self.imageNameList.count; index ++) {
        /// UIScrollView上的每一张图片
        UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake((index + 1) * kScreenWidth, 0, kScreenWidth, self.scrollView.frame.size.height)];
        imageView.image = [UIImage imageNamed:self.imageNameList[index]];
        
        [self.scrollView addSubview:imageView];
        self.scrollView.contentSize = CGSizeMake((index + 2) * self.scrollView.bounds.size.width, 0);
    }
    
    /// 在UIScrollView的最后面添加一张图片
    UIImageView *lastImageView = [[UIImageView alloc] initWithFrame:CGRectMake((self.imageNameList.count + 1) * kScreenWidth, 0, kScreenWidth, self.scrollView.frame.size.height)];
    /// 图片名是第一张图片
    lastImageView.image = [UIImage imageNamed:self.imageNameList.firstObject];
    [self.scrollView addSubview:lastImageView];
    
    /// 设置UIScrollView的偏移量
    self.scrollView.contentSize = CGSizeMake((self.imageNameList.count + 2) * self.scrollView.bounds.size.width, 0);
    
    /// 设置UIScrollView的起始偏移距离(将第一张图片跳过)
    self.scrollView.contentOffset = CGPointMake(kScreenWidth, 0);
    
    /// 图片总数
    self.pageControl.numberOfPages = self.imageNameList.count;
    self.pageControl.currentPage = 0;
}

#pragma mark - UIScrollViewDelegate
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    /// 当UIScrollView滑动到第一位停止时,将UIScrollView的偏移位置改变
    if (scrollView.contentOffset.x == 0) {
        scrollView.contentOffset = CGPointMake(self.imageNameList.count * kScreenWidth, 0);
        self.pageControl.currentPage = self.imageNameList.count;
    /// 当UIScrollView滑动到最后一位停止时,将UIScrollView的偏移位置改变
    } else if (scrollView.contentOffset.x == (self.imageNameList.count + 1)* kScreenWidth) {
        scrollView.contentOffset = CGPointMake(kScreenWidth, 0);
        self.pageControl.currentPage = 0;
    } else {
        self.pageControl.currentPage = scrollView.contentOffset.x / kScreenWidth - 1;
    }
}

#pragma mark - Get方法
-(UIScrollView *)scrollView {
    if (!_scrollView) {
        _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 200)];
        _scrollView.pagingEnabled = YES;
        _scrollView.clipsToBounds = NO;
        _scrollView.scrollEnabled = YES;
        _scrollView.delegate = self;
        _scrollView.bounces = NO;
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.showsVerticalScrollIndicator = NO;
        
        [self.view addSubview:_scrollView];
    }
    
    return _scrollView;
}

-(UIPageControl *)pageControl {
    if (!_pageControl) {
        _pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 150, kScreenWidth, 50)];
        
        _pageControl.pageIndicatorTintColor = [UIColor blackColor];
        _pageControl.currentPageIndicatorTintColor = [UIColor grayColor];
        
        [self.view addSubview:_pageControl];
    }
    
    return _pageControl;
}

@end

好了,如果大家使用的是swift语言,还可以参考这篇文章:https://www.jianshu.com/p/0ba33e59a784



作者:枫developer
链接:https://www.jianshu.com/p/7c4b79e5b123
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

 

第二种

#define SCREEN_HEIGHT [[UIScreen mainScreen] bounds].size.height // 主屏幕的高度

#define SCREEN_WIDTH  [[UIScreen mainScreen] bounds].size.width  // 主屏幕的宽度

#define SCROLLVIEW_HEIGHT _testScrollview.bounds.size.height //testScrollview高度

#define SHOW_IMAGECOUNT  3

#import "ViewController.h"

@interface ViewController ()<UIScrollViewDelegate>

{

    UIImageView *_leftImageView;

    UIImageView *_centerImageView;

    UIImageView *_rightImageView;

    int _currentImageIndex;

}

 

@property (weak, nonatomic) IBOutlet UIScrollView *testScrollview;

@property (weak, nonatomic) IBOutlet UIPageControl *testPageCon;

 

@property (nonatomic, strong) NSMutableArray *imageData;

@end

 

@implementation ViewController

 

 

/**

 *

 *  加载数据

 *

 */

-(NSMutableArray *)imageData{

    if (!_imageData) {

        _imageData = [NSMutableArray array];

        for (int i = 1; i < 8; i++) {

            [_imageData addObject:[UIImage imageNamed:[NSString stringWithFormat:@"%d.png",i]]];

        }

    }

    return _imageData;

}

 

- (void)viewDidLoad {

    

    [super viewDidLoad];

    

    [self setScrollview];

    

    [self setPageCon];

    

    [self addImageViews];

    

    [self loadDefaultImageView];

 

}

 

/**

 *

 *  设置scrollview

 *

 */

-(void)setScrollview{

    _testScrollview.contentSize = CGSizeMake(SCREEN_WIDTH * SHOW_IMAGECOUNT, SCROLLVIEW_HEIGHT);

    

    _testScrollview.delegate = self;

    

    [_testScrollview setContentOffset:CGPointMake(SCREEN_WIDTH, 0)];

    

    _testScrollview.pagingEnabled = YES;

    

    _testScrollview.showsHorizontalScrollIndicator=NO;

}

 

/**

 *

 *  设置分页控件

 *

 */

-(void)setPageCon{

    //注意此方法可以根据页数返回UIPageControl合适的大小

    CGSize size= [_testPageCon sizeForNumberOfPages:self.imageData.count];

    //设置颜色

    _testPageCon.bounds=CGRectMake(0, 0, size.width, size.height);

    _testPageCon.center = CGPointMake(SCREEN_WIDTH / 2, _testScrollview.frame.origin.y + SCROLLVIEW_HEIGHT - size.height);

    _testPageCon.pageIndicatorTintColor=[UIColor colorWithRed:193/255.0 green:219/255.0 blue:249/255.0 alpha:1];

    //设置当前页颜色

    _testPageCon.currentPageIndicatorTintColor=[UIColor colorWithRed:0 green:150/255.0 blue:1 alpha:1];

    //设置总页数

    _testPageCon.numberOfPages = self.imageData.count;

}

 

/**

 *

 *  添加图片

 *

 */

-(void)addImageViews{

    _leftImageView=[[UIImageView alloc]initWithFrame:CGRectMake(0, _testScrollview.frame.origin.x, SCREEN_WIDTH, SCROLLVIEW_HEIGHT)];

    _leftImageView.contentMode=UIViewContentModeScaleAspectFit;

    [_testScrollview addSubview:_leftImageView];

    

    _centerImageView=[[UIImageView alloc]initWithFrame:CGRectMake(SCREEN_WIDTH, _testScrollview.frame.origin.x, SCREEN_WIDTH, SCROLLVIEW_HEIGHT)];

    _centerImageView.contentMode=UIViewContentModeScaleAspectFit;

    [_testScrollview addSubview:_centerImageView];

    

    _rightImageView=[[UIImageView alloc]initWithFrame:CGRectMake(2*SCREEN_WIDTH, _testScrollview.frame.origin.x, SCREEN_WIDTH, SCROLLVIEW_HEIGHT)];

    _rightImageView.contentMode=UIViewContentModeScaleAspectFit;

    [_testScrollview addSubview:_rightImageView];

}

 

/**

 *

 *  加载默认图片

 *

 */

-(void)loadDefaultImageView{

    _leftImageView.image = self.imageData.lastObject;

    _centerImageView.image = self.imageData.firstObject;

    _rightImageView.image = self.imageData[1];

    _currentImageIndex = 0;

    

    _testPageCon.currentPage = _currentImageIndex;

}

 

/**

 *

 *  更新图片

 *

 */

-(void)updataImage{

    int leftImageIndex,rightImageIndex;

    CGPoint offset = [_testScrollview contentOffset];

    if (offset.x > SCREEN_WIDTH) { //向右滑动

        _currentImageIndex = (_currentImageIndex + 1) % self.imageData.count;

    }else if(offset.x < SCREEN_WIDTH){ //向左滑动

        _currentImageIndex = (int)(_currentImageIndex + self.imageData.count - 1) % self.imageData.count;

    }

    _centerImageView.image = self.imageData[_currentImageIndex];

    

    //重新设置左右图片

    leftImageIndex = (int)(_currentImageIndex + self.imageData.count - 1) % self.imageData.count;

    rightImageIndex = (_currentImageIndex + 1) % self.imageData.count;

 

    _leftImageView.image = self.imageData[leftImageIndex];

    _rightImageView.image = self.imageData[rightImageIndex];

}

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{

    //重新加载图片

    [self updataImage];

    //移动到中间

    [_testScrollview setContentOffset:CGPointMake(SCREEN_WIDTH, 0)];

    //设置分页

    _testPageCon.currentPage=_currentImageIndex;

}

github地址   

注:(部分图片采用了KenshinCui)

第三种

一、最终效果

二、原理说明

通过UICollectionView实现横向滚动轮播效果如下图所示,当移动到左右边缘后不可以继续移动

将CollectionView展开后的示意图如下:

思考:要使CollectionView滚动到左边缘和右边缘时均可以继续滚动,并且从左边缘向左滚动时显示的是最后一页,从右边缘向右滚动时显示的是第一页,可以在左边插入最后一页,在右边插入第一页,这样就可以滚动了。

示意图如下:

运行效果

现在还差最后一步了,当手动滚动到左侧第一页时,让CollectionView自动滚动到倒数第二页:

运行效果如下:

当手动滚动到最后一页时,让CollectionView自动滚动到正数第二页:

运行效果如下:

数据Setter方法如下:

-(void)setData:(NSArray<NSString *> *)data{
    _titles = [NSMutableArray arrayWithArray:data];
    //收尾分别插入一条数据
    [_titles addObject:data.firstObject];
    [_titles insertObject:data.lastObject atIndex:0];
    //_collectionView滚动到第二页的位置
    [_collectionView setContentOffset:CGPointMake(_collectionView.bounds.size.width, 0)];
    //设置_pageControl的显示页数
    _pageControl.numberOfPages = data.count;
}
通过代理方法更新CollectionView的滚动位置和PageControl的显示
 -(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    NSInteger page = scrollView.contentOffset.x/scrollView.bounds.size.width;
    NSLog(@"滚动到:%zd",page);
    if (page == 0) {//滚动到左边
        scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width * (_titles.count - 2), 0);
        _pageControl.currentPage = _titles.count - 2;
    }else if (page == _titles.count - 1){//滚动到右边
        scrollView.contentOffset = CGPointMake(scrollView.bounds.size.width, 0);
        _pageControl.currentPage = 0;
    }else{
        _pageControl.currentPage = page - 1;
    }
}
最终得到如下效果:

Github


--------------------- 
作者:孟宪亮 
来源:CSDN 
原文:https://blog.csdn.net/u013282507/article/details/60583959 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

//

//  NewPagedFlowView.m

//  dianshang

//

//  Created by sskh on 16/7/13.

//  Copyright © 2016年 Mars. All rights reserved.

//  Designed By PageGuo,

//  QQ:799573715

//  github:https://github.com/PageGuo/NewPagedFlowView

 

#import "HYNewPagedFlowView.h"

 

@interface HYNewPagedFlowView ()

 

@property (nonatomic, assign, readwrite) NSInteger currentPageIndex;

 

/**

 *  计时器用到的页数

 */

@property (nonatomic, assign) NSInteger page;

 

/**

 *  一页的尺寸

 */

@property (nonatomic,assign) CGSize pageSize;

 

@end

 

//子控制器的类名

static NSString *subviewClassName;

 

@implementation HYNewPagedFlowView

 

#pragma mark -

#pragma mark Private Methods

- (void)initialize{

    self.clipsToBounds = YES;

    

    self.needsReload = YES;

    self.pageCount = 0;

    self.isOpenAutoScroll = YES;

    self.isCarousel = YES;

    self.leftRightMargin = 20;

    self.topBottomMargin = 30;

    _currentPageIndex = 0;

    

    _minimumPageAlpha = 1.0;

    _autoTime = 5.0;

    

    self.visibleRange = NSMakeRange(0, 0);

    

    self.reusableCells = [[NSMutableArray alloc] initWithCapacity:0];

    self.cells = [[NSMutableArray alloc] initWithCapacity:0];

    

    self.scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];

    self.scrollView.scrollsToTop = NO;

    self.scrollView.delegate = self;

    self.scrollView.pagingEnabled = YES;

    self.scrollView.clipsToBounds = NO;

    self.scrollView.showsHorizontalScrollIndicator = NO;

    self.scrollView.showsVerticalScrollIndicator = NO;

    

    subviewClassName = @"PGIndexBannerSubiew";

    

    [self addSubview:self.scrollView];

    

}

 

- (void)setLeftRightMargin:(CGFloat)leftRightMargin {

    _leftRightMargin = leftRightMargin * 0.5;

    

}

 

- (void)setTopBottomMargin:(CGFloat)topBottomMargin {

    _topBottomMargin = topBottomMargin * 0.5;

}

 

- (void)startTimer {

    

    if (self.orginPageCount > 1 && self.isOpenAutoScroll && self.isCarousel) {

        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:self.autoTime target:self selector:@selector(autoNextPage) userInfo:nil repeats:YES];

        self.timer = timer;

        [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

 

    }

    

}

 

- (void)stopTimer {

    

    if (self.timer) {

        [self.timer invalidate];

        self.timer = nil;

    }

}

 

- (void)adjustCenterSubview {

    if (self.isOpenAutoScroll && self.orginPageCount > 0) {

        [_scrollView setContentOffset:CGPointMake(_pageSize.width * self.page, 0) animated:NO];

    }

}

 

#pragma mark --自动轮播

- (void)autoNextPage {

    

    self.page ++;

    

    

    

    switch (self.orientation) {

        case NewPagedFlowViewOrientationHorizontal:{

            

            [_scrollView setContentOffset:CGPointMake(self.page * _pageSize.width, 0) animated:YES];

            break;

        }

        case NewPagedFlowViewOrientationVertical:{

            

            [_scrollView setContentOffset:CGPointMake(0, self.page * _pageSize.height) animated:YES];

            

            break;

        }

        default:

            break;

    }

}

 

 

- (void)queueReusableCell:(PGIndexBannerSubiew *)cell{

    [_reusableCells addObject:cell];

}

 

- (void)removeCellAtIndex:(NSInteger)index{

    PGIndexBannerSubiew *cell = [_cells objectAtIndex:index];

    if ((NSObject *)cell == [NSNull null]) {

        return;

    }

    

    [self queueReusableCell:cell];

    

    if (cell.superview) {

        [cell removeFromSuperview];

    }

    

    [_cells replaceObjectAtIndex:index withObject:[NSNull null]];

}

 

- (void)refreshVisibleCellAppearance{

    

    if (_minimumPageAlpha == 1.0 && self.leftRightMargin == 0 && self.topBottomMargin == 0) {

        return;//无需更新

    }

    switch (self.orientation) {

        case NewPagedFlowViewOrientationHorizontal:{

            CGFloat offset = _scrollView.contentOffset.x;

            

            for (NSInteger i = self.visibleRange.location; i < self.visibleRange.location + _visibleRange.length; i++) {

                PGIndexBannerSubiew *cell = [_cells objectAtIndex:i];

                subviewClassName = NSStringFromClass([cell class]);

                CGFloat origin = cell.frame.origin.x;

                CGFloat delta = fabs(origin - offset);

                

                CGRect originCellFrame = CGRectMake(_pageSize.width * i, 0, _pageSize.width, _pageSize.height);//如果没有缩小效果的情况下的本该的Frame

                

                if (delta < _pageSize.width) {

                    

                    cell.coverView.alpha = (delta / _pageSize.width) * _minimumPageAlpha;

                    

                    CGFloat leftRightInset = self.leftRightMargin * delta / _pageSize.width;

                    CGFloat topBottomInset = self.topBottomMargin * delta / _pageSize.width;

                    

                    cell.layer.transform = CATransform3DMakeScale((_pageSize.width-leftRightInset*2)/_pageSize.width,(_pageSize.height-topBottomInset*2)/_pageSize.height, 1.0);

                    cell.frame = UIEdgeInsetsInsetRect(originCellFrame, UIEdgeInsetsMake(topBottomInset, leftRightInset, topBottomInset, leftRightInset));

 

                    

                } else {

                    cell.coverView.alpha = _minimumPageAlpha;

                    cell.layer.transform = CATransform3DMakeScale((_pageSize.width-self.leftRightMargin*2)/_pageSize.width,(_pageSize.height-self.topBottomMargin*2)/_pageSize.height, 1.0);

                    cell.frame = UIEdgeInsetsInsetRect(originCellFrame, UIEdgeInsetsMake(self.topBottomMargin, self.leftRightMargin, self.topBottomMargin, self.leftRightMargin));

 

                    

                }

 

            }

            break;

        }

        case NewPagedFlowViewOrientationVertical:{

            CGFloat offset = _scrollView.contentOffset.y;

            

            for (NSInteger i = self.visibleRange.location; i < self.visibleRange.location + _visibleRange.length; i++) {

                PGIndexBannerSubiew *cell = [_cells objectAtIndex:i];

                subviewClassName = NSStringFromClass([cell class]);

                CGFloat origin = cell.frame.origin.y;

                CGFloat delta = fabs(origin - offset);

                

                CGRect originCellFrame = CGRectMake(0, _pageSize.height * i, _pageSize.width, _pageSize.height);//如果没有缩小效果的情况下的本该的Frame

                

                if (delta < _pageSize.height) {

                    cell.coverView.alpha = (delta / _pageSize.height) * _minimumPageAlpha;

                    

                    CGFloat leftRightInset = self.leftRightMargin * delta / _pageSize.height;

                    CGFloat topBottomInset = self.topBottomMargin * delta / _pageSize.height;

                    

                    cell.layer.transform = CATransform3DMakeScale((_pageSize.width-leftRightInset*2)/_pageSize.width,(_pageSize.height-topBottomInset*2) / _pageSize.height, 1.0);

                    cell.frame = UIEdgeInsetsInsetRect(originCellFrame, UIEdgeInsetsMake(topBottomInset, leftRightInset, topBottomInset, leftRightInset));

                    cell.mainImageView.frame = cell.bounds;

                } else {

                    cell.coverView.alpha = _minimumPageAlpha;

                    cell.frame = UIEdgeInsetsInsetRect(originCellFrame, UIEdgeInsetsMake(self.topBottomMargin, self.leftRightMargin, self.topBottomMargin, self.leftRightMargin));

                    cell.mainImageView.frame = cell.bounds;

                }

    

            }

        }

        default:

            break;

    }

    

}

 

- (void)setPageAtIndex:(NSInteger)pageIndex{

    NSParameterAssert(pageIndex >= 0 && pageIndex < [_cells count]);

    

    PGIndexBannerSubiew *cell = [_cells objectAtIndex:pageIndex];

    

    if ((NSObject *)cell == [NSNull null]) {

        cell = [_dataSource flowView:self cellForPageAtIndex:pageIndex % self.orginPageCount];

        NSAssert(cell!=nil, @"datasource must not return nil");

        [_cells replaceObjectAtIndex:pageIndex withObject:cell];

        

        cell.tag = pageIndex % self.orginPageCount;

        [cell setSubviewsWithSuperViewBounds:CGRectMake(0, 0, _pageSize.width, _pageSize.height)];

        

        __weak __typeof(self) weakSelf = self;

        cell.didSelectCellBlock = ^(NSInteger tag, PGIndexBannerSubiew *cell) {

            [weakSelf singleCellTapAction:tag withCell:cell];

        };

        

        switch (self.orientation) {

            case NewPagedFlowViewOrientationHorizontal:

                cell.frame = CGRectMake(_pageSize.width * pageIndex, 0, _pageSize.width, _pageSize.height);

                break;

            case NewPagedFlowViewOrientationVertical:

                cell.frame = CGRectMake(0, _pageSize.height * pageIndex, _pageSize.width, _pageSize.height);

                break;

            default:

                break;

        }

        

        if (!cell.superview) {

            [_scrollView addSubview:cell];

        }

    }

}

 

 

- (void)setPagesAtContentOffset:(CGPoint)offset{

    //计算_visibleRange

    CGPoint startPoint = CGPointMake(offset.x - _scrollView.frame.origin.x, offset.y - _scrollView.frame.origin.y);

    CGPoint endPoint = CGPointMake(startPoint.x + self.bounds.size.width, startPoint.y + self.bounds.size.height);

    

    

    switch (self.orientation) {

        case NewPagedFlowViewOrientationHorizontal:{

            NSInteger startIndex = 0;

            for (int i =0; i < [_cells count]; i++) {

                if (_pageSize.width * (i +1) > startPoint.x) {

                    startIndex = i;

                    break;

                }

            }

            

            NSInteger endIndex = startIndex;

            for (NSInteger i = startIndex; i < [_cells count]; i++) {

                //如果都不超过则取最后一个

                if ((_pageSize.width * (i + 1) < endPoint.x && _pageSize.width * (i + 2) >= endPoint.x) || i+ 2 == [_cells count]) {

                    endIndex = i + 1;//i+2 是以个数,所以其index需要减去1

                    break;

                }

            }

            

            //可见页分别向前向后扩展一个,提高效率

            startIndex = MAX(startIndex - 1, 0);

            endIndex = MIN(endIndex + 1, [_cells count] - 1);

            

            //            self.visibleRange.location = startIndex;

            //            self.visibleRange.length = endIndex - startIndex + 1;

            self.visibleRange = NSMakeRange(startIndex, endIndex - startIndex + 1);

            for (NSInteger i = startIndex; i <= endIndex; i++) {

                [self setPageAtIndex:i];

            }

            

            for (int i = 0; i < startIndex; i ++) {

                [self removeCellAtIndex:i];

            }

            

            for (NSInteger i = endIndex + 1; i < [_cells count]; i ++) {

                [self removeCellAtIndex:i];

            }

            break;

        }

        case NewPagedFlowViewOrientationVertical:{

            NSInteger startIndex = 0;

            for (int i =0; i < [_cells count]; i++) {

                if (_pageSize.height * (i +1) > startPoint.y) {

                    startIndex = i;

                    break;

                }

            }

            

            NSInteger endIndex = startIndex;

            for (NSInteger i = startIndex; i < [_cells count]; i++) {

                //如果都不超过则取最后一个

                if ((_pageSize.height * (i + 1) < endPoint.y && _pageSize.height * (i + 2) >= endPoint.y) || i+ 2 == [_cells count]) {

                    endIndex = i + 1;//i+2 是以个数,所以其index需要减去1

                    break;

                }

            }

            

            //可见页分别向前向后扩展一个,提高效率

            startIndex = MAX(startIndex - 1, 0);

            endIndex = MIN(endIndex + 1, [_cells count] - 1);

            

            _visibleRange.location = startIndex;

            _visibleRange.length = endIndex - startIndex + 1;

            

            for (NSInteger i = startIndex; i <= endIndex; i++) {

                [self setPageAtIndex:i];

            }

            

            for (NSInteger i = 0; i < startIndex; i ++) {

                [self removeCellAtIndex:i];

            }

            

            for (NSInteger i = endIndex + 1; i < [_cells count]; i ++) {

                [self removeCellAtIndex:i];

            }

            break;

        }

        default:

            break;

    }

    

    

    

}

 

 

 

 

#pragma mark -

#pragma mark Override Methods

 

- (id)initWithFrame:(CGRect)frame

{

    self = [super initWithFrame:frame];

    if (self)

    {

        [self initialize];

    }

    return self;

}

 

- (id)initWithCoder:(NSCoder *)aDecoder

{

    self = [super initWithCoder:aDecoder];

    if (self)

    {

        [self initialize];

    }

    return self;

}

 

 

#pragma mark -

#pragma mark NewPagedFlowView API

 

- (void)reloadData {

    _needsReload = YES;

    

    //移除所有self.scrollView的子控件

    for (UIView *view in self.scrollView.subviews) {

        if ([NSStringFromClass(view.class) isEqualToString:subviewClassName] || [view isKindOfClass:[PGIndexBannerSubiew class]]) {

            [view removeFromSuperview];

        }

    }

    

    [self stopTimer];

 

    if (_needsReload) {

        //如果需要重新加载数据,则需要清空相关数据全部重新加载

        

        

        //重置pageCount

        if (_dataSource && [_dataSource respondsToSelector:@selector(numberOfPagesInFlowView:)]) {

            

            //原始页数

            self.orginPageCount = [_dataSource numberOfPagesInFlowView:self];

            

            //总页数

            if (self.isCarousel) {

                _pageCount = self.orginPageCount == 1 ? 1: [_dataSource numberOfPagesInFlowView:self] * 3;

            }else {

                _pageCount = self.orginPageCount == 1 ? 1: [_dataSource numberOfPagesInFlowView:self];

            }

            

            //如果总页数为0,return

            if (_pageCount == 0) {

                

                return;

            }

            

            if (self.pageControl && [self.pageControl respondsToSelector:@selector(setNumberOfPages:)]) {

                [self.pageControl setNumberOfPages:self.orginPageCount];

            }

        }

        

        //重置pageWidth

        _pageSize = CGSizeMake(self.bounds.size.width - 4 * self.leftRightMargin,(self.bounds.size.width - 4 * self.leftRightMargin) * 9 /16);

        if (self.delegate && self.delegate && [self.delegate respondsToSelector:@selector(sizeForPageInFlowView:)]) {

            _pageSize = [self.delegate sizeForPageInFlowView:self];

        }

        

        [_reusableCells removeAllObjects];

        _visibleRange = NSMakeRange(0, 0);

        

        //填充cells数组

        [_cells removeAllObjects];

        for (NSInteger index=0; index<_pageCount; index++)

        {

            [_cells addObject:[NSNull null]];

        }

        

        // 重置_scrollView的contentSize

        switch (self.orientation) {

            case NewPagedFlowViewOrientationHorizontal://横向

                _scrollView.frame = CGRectMake(0, 0, _pageSize.width, _pageSize.height);

                _scrollView.contentSize = CGSizeMake(_pageSize.width * _pageCount,0);

                CGPoint theCenter = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));

                _scrollView.center = theCenter;

                

                if (self.orginPageCount > 1) {

                    

                    if (self.isCarousel) {

                        

                        //滚到第二组

                        [_scrollView setContentOffset:CGPointMake(_pageSize.width * self.orginPageCount, 0) animated:NO];

                        

                        self.page = self.orginPageCount;

                        

                        //启动自动轮播

                        [self startTimer];

                        

                    }else {

                        //滚到开始

                        [_scrollView setContentOffset:CGPointMake(0, 0) animated:NO];

                        

                        self.page = self.orginPageCount;

                        

                    }

                    

                }

                

                break;

            case NewPagedFlowViewOrientationVertical:{

                _scrollView.frame = CGRectMake(0, 0, _pageSize.width, _pageSize.height);

                _scrollView.contentSize = CGSizeMake(0 ,_pageSize.height * _pageCount);

                CGPoint theCenter = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));

                _scrollView.center = theCenter;

                

                if (self.orginPageCount > 1) {

                    

                    if (self.isCarousel) {

                        //滚到第二组

                        [_scrollView setContentOffset:CGPointMake(0, _pageSize.height * self.orginPageCount) animated:NO];

                        

                        self.page = self.orginPageCount;

                        

                        //启动自动轮播

                        [self startTimer];

                    }else {

                        //滚到第二组

                        [_scrollView setContentOffset:CGPointMake(0, 0) animated:NO];

                        

                        self.page = self.orginPageCount;

                        

                    }

                    

                }

                

                break;

            }

            default:

                break;

        }

        

        _needsReload = NO;

    }

    

    

    [self setPagesAtContentOffset:_scrollView.contentOffset];//根据当前scrollView的offset设置cell

    

    [self refreshVisibleCellAppearance];//更新各个可见Cell的显示外貌

}

 

 

- (PGIndexBannerSubiew *)dequeueReusableCell{

    PGIndexBannerSubiew *cell = [_reusableCells lastObject];

    if (cell)

    {

        [_reusableCells removeLastObject];

    }

    

    return cell;

}

 

- (void)scrollToPage:(NSUInteger)pageNumber {

    if (pageNumber < _pageCount) {

        

        //首先停止定时器

        [self stopTimer];

        

        if (self.isCarousel) {

            

            self.page = pageNumber + self.orginPageCount;

            [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(startTimer) object:nil];

            [self performSelector:@selector(startTimer) withObject:nil afterDelay:0.5];

            

        }else {

            self.page = pageNumber;

        }

        

        switch (self.orientation) {

            case NewPagedFlowViewOrientationHorizontal:

                [_scrollView setContentOffset:CGPointMake(_pageSize.width * self.page, 0) animated:YES];

                break;

            case NewPagedFlowViewOrientationVertical:

                [_scrollView setContentOffset:CGPointMake(0, _pageSize.height * self.page) animated:YES];

                break;

        }

        [self setPagesAtContentOffset:_scrollView.contentOffset];

        [self refreshVisibleCellAppearance];

    }

}

 

#pragma mark -

#pragma mark hitTest

 

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

    if ([self pointInside:point withEvent:event]) {

        CGPoint newPoint = CGPointZero;

        newPoint.x = point.x - _scrollView.frame.origin.x + _scrollView.contentOffset.x;

        newPoint.y = point.y - _scrollView.frame.origin.y + _scrollView.contentOffset.y;

        if ([_scrollView pointInside:newPoint withEvent:event]) {

            return [_scrollView hitTest:newPoint withEvent:event];

        }

        

        return _scrollView;

    }

    

    return nil;

}

 

 

#pragma mark -

#pragma mark UIScrollView Delegate

 

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{

    

    if (self.orginPageCount == 0) {

        return;

    }

    

    NSInteger pageIndex;

    

    switch (self.orientation) {

        case NewPagedFlowViewOrientationHorizontal:

            pageIndex = (int)round(_scrollView.contentOffset.x / _pageSize.width) % self.orginPageCount;

            break;

        case NewPagedFlowViewOrientationVertical:

            pageIndex = (int)round(_scrollView.contentOffset.y / _pageSize.height) % self.orginPageCount;

            break;

        default:

            break;

    }

    

    if (self.isCarousel) {

        

        if (self.orginPageCount > 1) {

            switch (self.orientation) {

                case NewPagedFlowViewOrientationHorizontal:

                {

                    if (scrollView.contentOffset.x / _pageSize.width >= 2 * self.orginPageCount) {

                        

                        [scrollView setContentOffset:CGPointMake(_pageSize.width * self.orginPageCount, 0) animated:NO];

                        

                        self.page = self.orginPageCount;

                        

                    }

                    

                    if (scrollView.contentOffset.x / _pageSize.width <= self.orginPageCount - 1) {

                        [scrollView setContentOffset:CGPointMake((2 * self.orginPageCount - 1) * _pageSize.width, 0) animated:NO];

                        

                        self.page = 2 * self.orginPageCount;

                    }

                    

                }

                    break;

                case NewPagedFlowViewOrientationVertical:

                {

                    if (scrollView.contentOffset.y / _pageSize.height >= 2 * self.orginPageCount) {

                        

                        [scrollView setContentOffset:CGPointMake(0, _pageSize.height * self.orginPageCount) animated:NO];

                        

                        self.page = self.orginPageCount;

                        

                    }

                    

                    if (scrollView.contentOffset.y / _pageSize.height <= self.orginPageCount - 1) {

                        [scrollView setContentOffset:CGPointMake(0, (2 * self.orginPageCount - 1) * _pageSize.height) animated:NO];

                        self.page = 2 * self.orginPageCount;

                    }

                    

                }

                    break;

                default:

                    break;

            }

            

            

        }else {

            

            pageIndex = 0;

            

            

        }

    }

    

    

    [self setPagesAtContentOffset:scrollView.contentOffset];

    [self refreshVisibleCellAppearance];

    

    if (self.pageControl && [self.pageControl respondsToSelector:@selector(setCurrentPage:)]) {

        

        [self.pageControl setCurrentPage:pageIndex];

    }

    

    if (_delegate && [_delegate respondsToSelector:@selector(didScrollToPage:inFlowView:)] && _currentPageIndex != pageIndex && pageIndex >= 0) {

        [_delegate didScrollToPage:pageIndex inFlowView:self];

    }

    

    _currentPageIndex = pageIndex;

}

 

#pragma mark --将要开始拖拽

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {

    [self stopTimer];

}

 

#pragma mark --结束拖拽

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {

    [self startTimer];

}

 

#pragma mark --将要结束拖拽

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {

    

    if (self.orginPageCount > 1 && self.isOpenAutoScroll && self.isCarousel) {

        

        switch (self.orientation) {

            case NewPagedFlowViewOrientationHorizontal:

            {

                if (self.page == floor(_scrollView.contentOffset.x / _pageSize.width)) {

                    

                    self.page = floor(_scrollView.contentOffset.x / _pageSize.width) + 1;

                    

                }else {

                    

                    self.page = floor(_scrollView.contentOffset.x / _pageSize.width);

                }

            }

                break;

            case NewPagedFlowViewOrientationVertical:

            {

                if (self.page == floor(_scrollView.contentOffset.y / _pageSize.height)) {

                    

                    self.page = floor(_scrollView.contentOffset.y / _pageSize.height) + 1;

                    

                }else {

                    

                    self.page = floor(_scrollView.contentOffset.y / _pageSize.height);

                }

            }

                break;

            default:

                break;

        }

        

    }

}

 

//点击了cell

- (void)singleCellTapAction:(NSInteger)selectTag withCell:(PGIndexBannerSubiew *)cell {

    

    if (self.delegate && [self.delegate respondsToSelector:@selector(didSelectCell:withSubViewIndex:)]) {

        

        [self.delegate didSelectCell:cell withSubViewIndex:selectTag];

        

    }

}

 

//解决当父View释放时,当前视图因为被Timer强引用而不能释放的问题

- (void)willMoveToSuperview:(UIView *)newSuperview {

    if (!newSuperview) {

        [self stopTimer];

    }

}

 

//解决当timer释放后 回调scrollViewDidScroll时访问野指针导致崩溃

- (void)dealloc {

    _scrollView.delegate = nil;

}

 

@end

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值