当前位置: 移动技术网 > 移动技术>移动开发>IOS > 一行iOS代码实现图片无限轮播器

一行iOS代码实现图片无限轮播器

2019年07月24日  | 移动技术网移动技术  | 我要评论

最近一直在找实现图片无限轮播的方法,在网上也看了不少方法,大都不太合适,最终看到某it培训公司一位讲师用

uicollectionview:一行代码实现图片无限轮播器的方法,当然想一行代码实现轮播功能,前期还是有一些工作要做。下面就把这个方法分享给大家!

一、图片无限轮播实现效果图:


图片无限轮播.gif

二、实现原理与分析:

假设有三张图片0、1、2,想要实现无限轮播,我们可以将uicollectionview的cell个数设为图片的个数 x 3,也就是把三张图片重复添加到9个cell中,可以把无限轮播分解成五种特殊的状态(五个临界点),轮播开始时为初始状态,在定时器的作用下依次滚动到最后一个cell,此时为右临界状态显示的是第2张图片,若想继续无缝滚动到第0图片,我们可以偷偷的将collectionview滚动到第三个cell上,可以看第四幅转态图此时显示的依然是第2张图片,这样再次滚动就是第0张图,这样就实现了cell向左滚动的无限循环轮播;向右滚动的原理一样,就是第三幅图到第五幅图的变化。


初始界状态.png


右临界状态.png


左临界状态.png


paste_image.png


paste_image.png

三、代码:


类文件.png

  •  jfweaktimertargetobject继承自nsobject
  • jfloopview继承自uiview
  • jfloopviewcell继承自uicollectionviewcell
  • jfloopviewlayout继承自uicollectionviewflowlayout
  • jfmainviewcontroller继承自uiviewcontroller

jfweaktimertargetobject重写定时器nstimer的+ (nstimer *)scheduledtimerwithtimeinterval:(nstimeinterval)ti target:(id)atarget selector:(sel)aselector userinfo:(nullable id)userinfo repeats:(bool)yesorno;类方法的目的是:避免当定时器强引用jfloopview类,jfloopview无法被释放的问题。

jfweaktimertargetobject.h文件

#import <foundation/foundation.h>
@interface jfweaktimertargetobject : nsobject
+ (nstimer *)scheduledtimerwithtimeinterval:(nstimeinterval)ti target:(id)atarget selector:(sel)aselector userinfo:(nullable id)userinfo repeats:(bool)yesorno;

@end

jfweaktimertargetobject.m文件

#import "jfweaktimertargetobject.h"

@interface jfweaktimertargetobject ()

@property (nonatomic, weak) id target;
@property (nonatomic, assign) sel selector;

@end

@implementation jfweaktimertargetobject

+ (nstimer *)scheduledtimerwithtimeinterval:(nstimeinterval)ti target:(id)atarget selector:(sel)aselector userinfo:(nullable id)userinfo repeats:(bool)yesorno {
 //创建当前类对象
 jfweaktimertargetobject *object = [[jfweaktimertargetobject alloc] init];
 object.target = atarget;
 object.selector = aselector;
 return [nstimer scheduledtimerwithtimeinterval:ti target:object selector:@selector(fire:) userinfo:userinfo repeats:yesorno];
}

- (void)fire:(id)obj {
 [self.target performselector:self.selector withobject:obj];
}

@end

jfloopview.h文件

#import <uikit/uikit.h>

@interface jfloopview : uiview

//jfloopview初始化方法
- (instancetype)initwithimagearray:(nsarray *)imagearray;

@end

jfloopview.m文件

#import "jfloopview.h"

#import "jfloopviewlayout.h"
#import "jfloopviewcell.h"
#import "jfweaktimertargetobject.h"

@interface jfloopview () <uicollectionviewdelegate, uicollectionviewdatasource>

@property (nonatomic, strong) uicollectionview *collectionview;
@property (nonatomic, strong) uipagecontrol *pagecontrol;
@property (nonatomic, strong) nsarray *imagearray;
@property (nonatomic, weak) nstimer *timer;

@end

static nsstring *id = @"loopviewcell";

@implementation jfloopview

- (instancetype)initwithimagearray:(nsarray *)imagearray {
 if (self = [super init]) {
 uicollectionview *collectionview = [[uicollectionview alloc] initwithframe:cgrectzero collectionviewlayout:[[jfloopviewlayout alloc] init]];
 [collectionview registerclass:[jfloopviewcell class] forcellwithreuseidentifier:id];
 collectionview.datasource = self;
 collectionview.delegate = self;
 [self addsubview:collectionview];

 self.collectionview = collectionview;
 self.imagearray = imagearray;

 //添加分页器
 [self addsubview:self.pagecontrol];

 //回到主线程刷新ui
 dispatch_async(dispatch_get_main_queue(), ^{
 //设置滚动的初始状态在
 [self.collectionview scrolltoitematindexpath:[nsindexpath indexpathforitem:self.imagearray.count insection:0] atscrollposition:uicollectionviewscrollpositionleft animated:no];

 //添加定时器
 [self addtimer];
 });

 }
 return self;
}

/// 懒加载pagecontrol
- (uipagecontrol *)pagecontrol {
 if (!_pagecontrol) {
 _pagecontrol = [[uipagecontrol alloc] initwithframe:cgrectmake(0, 220, 0, 30)];
 _pagecontrol.numberofpages = self.imagearray.count;
 _pagecontrol.pageindicatortintcolor = [uicolor orangecolor];
 _pagecontrol.currentpageindicatortintcolor = [uicolor purplecolor];
 }
 return _pagecontrol;
}

#pragma mark --- uicollectionviewdatasource 数据源方法
- (nsinteger)collectionview:(uicollectionview *)collectionview numberofitemsinsection:(nsinteger)section {
 return self.imagearray.count * 3;
}

- (uicollectionviewcell *)collectionview:(uicollectionview *)collectionview cellforitematindexpath:(nsindexpath *)indexpath {
 jfloopviewcell *cell = [collectionview dequeuereusablecellwithreuseidentifier:id forindexpath:indexpath];
 cell.imagename = self.imagearray[indexpath.item % self.imagearray.count];
 return cell;
}

#pragma mark ---- uicollectionviewdelegate

/// 滚动完毕就会调用(如果不是人为拖拽scrollview导致滚动完毕,才会调用这个方法)
- (void)scrollviewdidendscrollinganimation:(uiscrollview *)scrollview {
 [self scrollviewdidenddecelerating:scrollview];
}

/// 当滚动减速时调用
- (void)scrollviewdidenddecelerating:(uiscrollview *)scrollview {
 cgfloat offsetx = scrollview.contentoffset.x;
 nsinteger page = offsetx / scrollview.bounds.size.width;

 //手动滚动到左边临界状态
 if (page == 0) {
 page = self.imagearray.count;
 self.collectionview.contentoffset = cgpointmake(page * scrollview.frame.size.width, 0);
 //滚动到右临界状态
 }else if (page == [self.collectionview numberofitemsinsection:0] - 1) {
 page = self.imagearray.count - 1;
 self.collectionview.contentoffset = cgpointmake(page * scrollview.frame.size.width, 0);
 }

 //设置uipagecontrol当前页
 nsinteger currentpage = page % self.imagearray.count;
 self.pagecontrol.currentpage =currentpage;
 //添加定时器
 [self addtimer];
}

///手指开始拖动时调用
- (void)scrollviewwillbegindragging:(uiscrollview *)scrollview {
 //移除定时器
 [self removetimer];
}

/// 添加定时器
- (void)addtimer {
 if (self.timer) return;
 self.timer = [jfweaktimertargetobject scheduledtimerwithtimeinterval:1.5 target:self selector:@selector(nextimage) userinfo:nil repeats:yes];
 [[nsrunloop currentrunloop] addtimer:self.timer formode:nsrunloopcommonmodes];
}

/// 移除定时器
- (void)removetimer {
 [self.timer invalidate];
 self.timer = nil;
}

/// 切换到下一张图片
- (void)nextimage {
 cgfloat offsetx = self.collectionview.contentoffset.x;
 nsinteger page = offsetx / self.collectionview.bounds.size.width;
 [self.collectionview setcontentoffset:cgpointmake((page + 1) * self.collectionview.bounds.size.width, 0) animated:yes];
}

- (void)layoutsubviews {
 [super layoutsubviews];
 self.collectionview.frame = self.bounds;
}

- (void)dealloc {
 [self removetimer];
}

@end

jfloopviewcell.h文件

#import <uikit/uikit.h>
@interface jfloopviewcell : uicollectionviewcell
@property (nonatomic, copy) nsstring *imagename;
@end

jfloopviewcell.m文件

#import "jfloopviewcell.h"
@interface jfloopviewcell ()
@property (nonatomic, weak) uiimageview *iconview;

@end

@implementation jfloopviewcell

- (instancetype)initwithframe:(cgrect)frame {
 if (self = [super initwithframe:frame]) {
 uiimageview *iconview = [[uiimageview alloc] init];
 [self addsubview:iconview];
 self.iconview = iconview;
 }
 return self;
}

- (void)setimagename:(nsstring *)imagename {
 _imagename = imagename;
 self.iconview.image = [uiimage imagenamed:imagename];
}

- (void)layoutsubviews {
 [super layoutsubviews];
 self.iconview.frame = self.bounds;
}

@end

jfloopviewlayout.h文件

#import <uikit/uikit.h>

@interface jfloopviewlayout : uicollectionviewflowlayout

@end

jfloopviewlayout.m文件

#import "jfloopviewlayout.h"

@implementation jfloopviewlayout

/// 准备布局
- (void)preparelayout {
 [super preparelayout];

 //设置item尺寸
 self.itemsize = self.collectionview.frame.size;
 //设置滚动方向
 self.scrolldirection = uicollectionviewscrolldirectionhorizontal;
 //设置分页
 self.collectionview.pagingenabled = yes;

 //设置最小间距
 self.minimumlinespacing = 0;
 self.minimuminteritemspacing = 0;

 //隐藏水平滚动条
 self.collectionview.showshorizontalscrollindicator = no;
}

@end

jfmainviewcontroller.h文件

#import <uikit/uikit.h>

@interface jfmainviewcontroller : uiviewcontroller

@end

jfmainviewcontroller.m文件

#import "jfmainviewcontroller.h"

#import "jfloopview.h"

@interface jfmainviewcontroller ()

@property (nonatomic, strong) jfloopview *loopview;

@end

@implementation jfmainviewcontroller

- (void)viewdidload {
 [super viewdidload];

 //关闭自动调整滚动视图
 self.automaticallyadjustsscrollviewinsets = no;
}

- (void)viewwillappear:(bool)animated {
 [super viewwillappear:animated];
 self.navigationcontroller.navigationbar.hidden = yes;
}

- (void)loadview {
 [super loadview];

 //设置图片数据
 nsarray *imagearray = @[@"srcoll_01",@"srcoll_02",@"srcoll_03"];

 //此行代码实现无限轮播
 _loopview = [[jfloopview alloc] initwithimagearray:imagearray];

 //设置loopview的frame
 _loopview.frame = cgrectmake(0, 0, [uiscreen mainscreen].bounds.size.width, 250);

 [self.view addsubview:self.loopview];
}

- (void)didreceivememorywarning {
 [super didreceivememorywarning];
 // dispose of any resources that can be recreated.
}

@end

注意:如果你的控制器有uinavigationbar,且隐藏了navigationbar,一定要记得设置self.automaticallyadjustsscrollviewinsets = no; automaticallyadjustsscrollviewinsets是干嘛的呢?简单点说就是automaticallyadjustsscrollviewinsets根据所在界面的status bar、navigationbar、与tabbar的高度,自动调整scrollview的 inset,设置为no,不让viewcontroller调整,我们自己修改布局即可。如果不设置为no就可能出现下面的情况,自动滚动和拖动的时候imageview的位置会变化。


图片无限轮播bug展示.gif

四、总结:

1、实现无限轮播器的主要控件是uicollectionview和uipagecontrol,
2、封装好工具类以后再使用时一行_loopview = [[jfloopview alloc] initwithimagearray:imagearray];代码,然后设置frame就可以复用无限轮播器。
3、合理切换图片和图片排列的方法,加上恰当地使用uicollectionview提供的代理方法就可以完美的实现无限轮播器。

写在最后:

下一篇文章讲用uicollectionview实现电商app首页的方法:


电商app的首页展示.gif

如果你有好的方法敬请分享,感谢你的阅读!欢迎关注和评论!

源码地址:链接: https://pan.baidu.com/s/1nv5fqzj 密码: qz3u

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网