最终效果图:
实现思路1:使用2个view,一个是当前的view,一个是重用的view
此方法中无论数据源有多少个,UIScrollView只保留其中2个视图,currentView和reUsingView,默认,scrollView的滚动范围3个width,并且currentView始终摆在中间位置(即x始终摆在一个width处)
当你滑动UIScrollView的时候,无非是向前滑动,或者是向后滑动。当你每次开始滑动的时候,我根据妳滑动的方向,切换reUsingView视图里面的内容。
然后在妳停止滚动的时候,重置currentView
看看下面的区别:
[objc] view plain copy
- (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView {
[_scrollView setContentOffset:CGPointMake(_scrollView.frame.size.width, 0) animated:YES];
}
我们发现每一次滑动完成之后,UIScrollView总是重新切换回默认的中这一个视图。
<span >//
// ViewController.m
// SuJi
//
// Created by beyond on 16/7/9.
// Copyright © 2016年 beyond. All rights reserved.
//
#import "ViewController.h"
#import "UIView+Frame.h"
@interface ViewController ()<UIScrollViewDelegate>
{
UIScrollView *_scrollView;
// 无限滚动的数据源数组
NSArray *_slideImageArr;
// 用来循环滚动的两个view
UIImageView *_currentImageView;
UIImageView *_resuingImageView;
int _imageIndex;
BOOL _isScrollDirectionNext;
// 起始滚动 和 结束滚动的时候,不足一页,则不做任何处理
CGFloat _startPositionX;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
_slideImageArr = @[@"",@"",@"",@""];
[self addSubviews];
}
- (void)addSubviews
{
[self addBgImageView];
[self addScrollView];
}
- (void)addBgImageView
{
UIImageView *bgView = [[UIImageView alloc]init];
bgView.frame = self.view.frame;
UIImage *bgImage = [UIImage imageNamed:@"IMG_4397.JPG"];
CGFloat top = 25; // 顶端盖高度
CGFloat bottom = 25 ; // 底端盖高度
CGFloat left = 10; // 左端盖宽度
CGFloat right = 10; // 右端盖宽度
UIEdgeInsets insets = UIEdgeInsetsMake(top, left, bottom, right);
// 指定为拉伸模式,伸缩后重新赋值
bgImage = [bgImage resizableImageWithCapInsets:insets resizingMode:UIImageResizingModeStretch];
bgView.image = bgImage;
[self.view addSubview:bgView];
UIImageView *bgImageView = [[UIImageView alloc]init];
bgImageView.frame = self.view.frame;
bgImageView.contentMode = UIViewContentModeScaleAspectFit;
bgImageView.image = [UIImage imageNamed:@"IMG_4398.png"];
[self.view addSubview:bgImageView];
// 加个灰色的蒙版
UIView *grayView = [[UIView alloc]initWithFrame:bgImageView.frame];
grayView.backgroundColor = [UIColor blackColor];
grayView.alpha = 0.6;
[self.view addSubview:grayView];
}
- (void)addScrollView
{
_scrollView = [[UIScrollView alloc]init];
_scrollView.pagingEnabled = YES;
_scrollView.delegate = self;
[_scrollView addSubview:[UIButton buttonWithType:UIButtonTypeContactAdd]];
_scrollView.frame = self.view.frame;
_scrollView.contentSize = CGSizeMake(0, self.view.frame.size.height + 1);
_scrollView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_scrollView];
// 初始化设置
CGFloat width = _scrollView.frame.size.width;
_scrollView.contentSize = CGSizeMake(3 * width, 0);
_scrollView.contentOffset = CGPointMake(width, 0);
/*
第二种方式:只有两个view
1个当前的View及1个缓冲的View
将当前的View放在中间。
判断滑动的位置,优先去缓冲的View找
优点:对内存消耗少
*/
UIImageView *currentImageView = [[UIImageView alloc] init];
currentImageView.frame = CGRectMake(width, 0, width, _scrollView.frame.size.height);
currentImageView.image = [UIImage imageNamed:@"00.jpg"];
[_scrollView addSubview:currentImageView];
_currentImageView = currentImageView;
// 初始化 缓冲的ImageView
[self setUpReusingImageView];
_imageIndex = 0;
}
/** @programmer beyond@xsism.com
* @brief 初始化 缓冲的ImageView
* @param <#无#>
* @return <#无#>
*/
- (void)setUpReusingImageView
{
CGFloat width = _scrollView.frame.size.width;
UIImageView *resuingImageView = [[UIImageView alloc] init];
resuingImageView.frame = CGRectMake(0, 0, width, _scrollView.frame.size.height);
[_scrollView addSubview:resuingImageView];
_resuingImageView = resuingImageView;
}
#pragma mark - scrollView代理
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
_startPositionX = scrollView.contentOffset.x;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if (scrollView.contentOffset.x > _currentImageView.frame.origin.x) {
// 向右滚动时
NSInteger val = _imageIndex + 1;
if (_imageIndex >= [_slideImageArr count] - 1) {
val = 0;
}
// 取缓冲区的View
NSString *imgName = [NSString stringWithFormat:@"0%zd.jpg",val];
_resuingImageView.image = [UIImage imageNamed:imgName];
_resuingImageView.x = CGRectGetMinX(_currentImageView.frame) + _currentImageView.width;
_isScrollDirectionNext = YES;
}else{
// 向左滚动的时候
NSInteger val = _imageIndex - 1;
if (val < 0) {
val = [_slideImageArr count]-1;
}
_resuingImageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"0%zd.jpg",val]];
_resuingImageView.x = 0;
_isScrollDirectionNext = NO;
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
scrollView.userInteractionEnabled = YES;
if (abs(_startPositionX - scrollView.contentOffset.x) < scrollView.width) {
return;
}
// 是否是往右边滑动
if (_isScrollDirectionNext) {
_imageIndex++;
}else{
_imageIndex--;
}
// 补全
if (_imageIndex < 0) {
_imageIndex = [_slideImageArr count]-1;
} else if(_imageIndex > [_slideImageArr count]-1){
_imageIndex = 0;
}
_currentImageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"0%zd.jpg",_imageIndex]];
_currentImageView.frame = CGRectMake(_scrollView.width, 0, _scrollView.width, _scrollView.frame.size.height);
[_scrollView setContentOffset:CGPointMake(_scrollView.frame.size.width, 0) animated:NO];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
scrollView.userInteractionEnabled = NO;
}
@end
</span>
实现方式2:
使用CollectionView实现,带pageContrl + timer定时器
思路,使用1组,但是告诉控制器有modelArrCount*5000个item,并且cellForRow时,创建根据index取模modelArrCount,取出数据源(实际只有8个),并且item的宽度就是一个屏幕的宽度
代码片段:
</pre><pre>
#import "BeyondViewController.h"
// 快速Frame
#import "UIView+Frame.h"
#import "BeyondNewsCell.h"
#import "BeyondNews.h"
#import "MJExtension.h"
// 只有一组,但是该组 有5000*8行
#define TotalItems (5000 * self.newses.count)
// 第一次出现的时候,didAppear时,就出现在中间,左边对齐
#define MiddleItem (NSUInteger)(TotalItems * 0.5)
@interface BeyondViewController () <UICollectionViewDataSource, UICollectionViewDelegate>
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (weak, nonatomic) IBOutlet UIPageControl *pageCtrl;
@property (strong, nonatomic) NSArray *newses;
@property (strong, nonatomic) NSTimer *timer;
@end
@implementation BeyondViewController
#pragma mark - 懒加载
- (NSArray *)newses
{
if (!_newses) {
self.newses = [BeyondNews objectArrayWithFilename:@"newses.plist"];
// 总页数
self.pageCtrl.numberOfPages = self.newses.count;
}
return _newses;
}
#pragma mark - 生命周期
- (void)viewDidLoad
{
[super viewDidLoad];
// 注册cell【UICollectionViewCell不让代码创建】
[self.collectionView registerNib:[UINib nibWithNibName:@"BeyondNewsCell" bundle:nil] forCellWithReuseIdentifier:@"news"];
// 添加定时器
[self addTimer];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:MiddleItem inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
}
#pragma mark - 时钟方法
- (void)addTimer
{
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(schedule) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)removeTimer
{
[self.timer invalidate];
self.timer = nil;
}
// 定时器方法
- (void)schedule
{
// 得到当前显示的item
NSIndexPath *visiablePath = [[self.collectionView indexPathsForVisibleItems] firstObject];
NSUInteger visiableItem = visiablePath.item;
if ((visiablePath.item % self.newses.count) == 0) { // 第0张图片
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:MiddleItem inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
visiableItem = MiddleItem;
}
// 滚动到下一个item
NSUInteger nextItem = visiableItem + 1;
[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:nextItem inSection:0] atScrollPosition:UICollectionViewScrollPositionLeft animated:YES];
}
#pragma mark - 数据源和代理方法
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return TotalItems;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = @"news";
// 直接取
BeyondNewsCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
// UICollectionViewCell没有根据id进行创建方法,只能 从xib加载
// UICollectionViewFlowLayout *layout 中 可以指定cell的高度
cell.news = self.newses[indexPath.item % self.newses.count];
return cell;
}
// 重要,监听代理 停止滚动的事件
- (void)collectionView:(UICollectionView *)collectionView didEndDisplayingCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath
{
NSIndexPath *visiablePath = [[collectionView indexPathsForVisibleItems] firstObject];
self.pageCtrl.currentPage = visiablePath.item % self.newses.count;
}
#pragma mark - scrollView代理
// 开始拖拽
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
[self removeTimer];
}
// 停止拖拽
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self addTimer];
}
@end
使用到的分类:
//
// Frame.h
// 08-无限滚动
//
// Created by beyond on 15-3-27.
// Copyright (c) 2015年 itcast. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIView (Frame)
@property (assign, nonatomic) CGFloat x;
@property (assign, nonatomic) CGFloat y;
@property (assign, nonatomic) CGFloat width;
@property (assign, nonatomic) CGFloat height;
@property (assign, nonatomic) CGSize size;
@property (assign, nonatomic) CGPoint origin;
@end
//
// Frame.m
// 08-无限滚动
//
// Created by beyond on 15-3-27.
// Copyright (c) 2015年 itcast. All rights reserved.
//
#import "UIView+Frame.h"
@implementation UIView (Frame)
- (void)setX:(CGFloat)x
{
CGRect frame = self.frame;
frame.origin.x = x;
self.frame = frame;
}
- (CGFloat)x
{
return self.frame.origin.x;
}
- (void)setY:(CGFloat)y
{
CGRect frame = self.frame;
frame.origin.y = y;
self.frame = frame;
}
- (CGFloat)y
{
return self.frame.origin.y;
}
- (void)setWidth:(CGFloat)width
{
CGRect frame = self.frame;
frame.size.width = width;
self.frame = frame;
}
- (CGFloat)width
{
return self.frame.size.width;
}
- (void)setHeight:(CGFloat)height
{
CGRect frame = self.frame;
frame.size.height = height;
self.frame = frame;
}
- (CGFloat)height
{
return self.frame.size.height;
}
- (void)setSize:(CGSize)size
{
CGRect frame = self.frame;
frame.size = size;
self.frame = frame;
}
- (CGSize)size
{
return self.frame.size;
}
- (void)setOrigin:(CGPoint)origin
{
CGRect frame = self.frame;
frame.origin = origin;
self.frame = frame;
}
- (CGPoint)origin
{
return self.frame.origin;
}
@end
MainStoryBoard,在FlowLayout中指定item的size
Xib创建CollectionCell,注意指定重用ID