两个imageView实现图片轮播

前言

在不少的项目中,都会用到图片轮播这个功能,现在网上关于图片轮播的轮子也层出不穷,千奇百怪,笔者根据自己的思路,用两个imageView也实现了图片轮播,这里给大家介绍笔者的主要思路以及大概步骤。

轮播实现步骤

层级结构

最底层是一个UIView,上面有一个UIScrollView和UIPageControl,scrollView上有两个UIImageView,imageView的宽高=scrollView的宽高=view的宽高

轮播原理

假设轮播控件的宽为x,高为y,我们设置scrollView的contentSize的宽度为3x,并且让scrollView在x方向偏移量为x,即显示中间内容

    scrollView.contentSize = CGSizeMake(3x, y);
    scrollView.contentOffset = CGPointMake(x, 0);

接下来使用代理方法scrollViewDidScroll来监听scrollView的滚动,定义一个枚举来记录滚动的方向

    typedef NS_ENUM(NSInteger, Direction) {
        DirectionNone = 1 << 0,
        DirectionLeft = 1 << 1,
        DirectionRight = 1 << 2
    };
    // 滚动方向
    @property (nonatomic, assign) Direction direction;
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
        CGFloat offX = scrollView.contentOffset.x;
        self.direction = offX > self.width ? DirectionLeft : offX < self.width ? DirectionRight : DirectionNone;
    }

重写direction的setter方法,根据滚动方向来设置下一张图片的显示,如果是往左边滚动,那么下一张图片的位置应该在右边,如果是往右滚动,那么下一张图片的位置应该在左边。(ps:此处应该注意滚动到第一张和最后一张的边界情况)

    #pragma mark - 设置滚动方向
    - (void)setDirection:(Direction)direction {
        if (_direction == direction) return;
        _direction = direction;
        if (_direction == DirectionNone) return;
        if (_direction == DirectionRight) { // 如果是向右滚动
            self.nextImageView.frame = CGRectMake(0, 0, self.width, self.height);
            self.nextIndex = self.currentIndex - 1;
            if (self.nextIndex < 0) self.nextIndex = _images.count - 1;
        }else if (_direction == DirectionLeft){ // 如果是向左边滚动
            self.nextImageView.frame = CGRectMake(CGRectGetMaxX(_currentImageView.frame), 0, self.width, self.height);
            self.nextIndex = (self.currentIndex + 1) % _images.count;
        }
        self.nextImageView.image = self.images[self.nextIndex];
    }

通过代理方法scrollViewDidEndDecelerating来监听滚动结束,结束后,会变成以下两种情况:

  • 左滚之后
  • 右滚之后

此时,scrollView的偏移量为0或者2x两种情况,我们通过代码再次将scrollView的偏移量设置为x,并且将nextImageView的图片修改为赋值给currentImageView的图片

    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
        [self pauseScroll];
    }
    - (void)pauseScroll {
        // 等于1表示没有滚动
        if (self.scrollView.contentOffset.x / self.width == 1) return;
        self.currentIndex = self.nextIndex;
        self.pageControl.currentPage = self.currentIndex;
        self.currentImageView.frame = CGRectMake(self.width, 0, self.width, self.height);
        self.descLabel.text = self.describeArray[self.currentIndex];
        self.currentImageView.image = self.nextImageView.image;
        self.scrollView.contentOffset = CGPointMake(self.width, 0);
    }

这样之后,我们看到的还是currentImageView,只是图片显示的是下一张的图片或者上一张的图片,又回到了最初的样子。

自动滚动

轮播的功能实现了,接下来就是添加定时器让它自动滚动了。

    // 开启定时器
    - (void)startTimer {
        // 如果只有一张,直接放回,不需要开启定时器
        if (_images.count <= 1) return;
        // 如果定时器已经开启,则先停止再开启
        if (self.timer) [self stopTimer];
        self.timer = [NSTimer timerWithTimeInterval:_time < 1 ? DEFAULTTIME : _time target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    }
    // 下一页
    - (void)nextPage {
        [self.scrollView setContentOffset:CGPointMake(self.width * 2, 0) animated:YES];
    }

注意

setContentOffset:animated:方法执行完毕后不会调用scrollview的scrollViewDidEndDecelerating方法,但是会调用scrollViewDidEndScrollingAnimation方法,因此我们要在该方法中调用pauseScroll

    - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
        [self pauseScroll];
    }

拖拽时停止定时器

当我们手动拖拽的时候,需要停止自动滚动,此时我们只需要关闭定时器就行了,当我们拖拽结束的时候,重新启动定时器

    - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
        [self stopTimer];
    }
    - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
        [self startTimer];
    }

加载图片

在实际开发中,我们很少自动轮播本地的图片,大部分都是服务器获取的图片url,也有可能既有本地图片,又有网络图片,那么该如何加载呢?

  1. 定义一个imageArr用来接收外界传进来的数组(可以是图片,也可以是网络图片路径,可以图片和路径混合)
  2. 定义一个images用来存储图片(只装图片).判断外界传进来的数组,如果是图片,直接添加到images,如果是连接,先添加一张默认的占位图
  3. 定义一个imageDic用来缓存图片的字典,key为图片URL
  4. 定义一个operationDic用来保存下载操作的字典,key为图片URL

图片缓存策略(SDWebImage的思路)

下载图片,先从缓存中取,如果有,则替换之前的占位图片,如果没有,去沙盒中取,如果有,替换占位图片,并添加到缓存中,如果没有,开启异步线程下载

监听图片点击

在实际开发中,通常轮播图都有点击图片跳转到对应的内容的操作,因此需要监听图片的点击,提供两种思路:

  • 通过block:
    1. 定义一个block给外界
    2. 打开currentImageView的用户交互
    3. 给currentImageView添加一个点击手势
    4. 在点击手势响应方法里面调用block,并传入图片所在的索引
  • 通过代理:
    1. 定义一个协议方法,设置一个代理属性
    2. 打开currentImageView的用户交互
    3. 给currentImageView添加一个点击手势
    4. 在点击手势响应方法里面用代理调用协议方法,

结束语

上面是笔者实现轮播图的思路以及部分代码,需要源码的请戳这里,如果在使用中发现有bug,欢迎提出!如果觉得好用,记得献上你的star哦!

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏跟着阿笨一起玩NET

WinForm开发中针对TreeView控件改变当前选择节点的字体与颜色

本文转载:http://www.cnblogs.com/umplatform/archive/2012/08/29/2660240.html

1781
来自专栏DeveWork

巧用CSS3 :target 伪类制作Dropdown下拉菜单(无JS)

:target 是CSS3 中新增的一个伪类,用以匹配当前页面的URI中某个标志符的目标元素(比如说当前页面URL下添加#comment就会定位到id=“com...

2328
来自专栏macOS 开发学习

Mac开发跬步积累(二):NSViewController 转场动画精耕细作

在macOS 10.10之后,关于NSViewController,苹果公司专门在一个extension中提供了四个方法用来处理控制器之间的关系以及切换转场处理...

1734
来自专栏更流畅、简洁的软件开发方式

给自定义控件(Web Control)添加事件的几种方法。前两种方法可以不实现IPostBackEventHandler

    写自定义控件已经好久了,也有几个用得时间比较长的,但是对于“事件”一直是比较模糊,没有很详细的理解。     最近升级分页控件,由于原来使用的是VB...

2227
来自专栏Java后端技术

Javascript中最常用的55个经典技巧(转)

1. oncontextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键  <table border oncon...

1152
来自专栏云瓣

React之父子组件传递和其它一些要点

react是R系技术栈中最基础同时也是最核心的一环,2年不到获取了62.5k star(截止到目前),足可见其给力程度。下面对一些react日常开发中的注意事项...

3998
来自专栏九彩拼盘的叨叨叨

前端组件整理

4112
来自专栏hightopo

基于HTML5 Canvas实现用户交互

1082
来自专栏非著名程序员

基础篇章:关于 React Native 之 KeyboardAvoidingView 组件的讲解

友情提示:RN学习,从最基础的开始,大家不要嫌弃太基础,会的同学请自行略过,希望不要耽误已经会的同学的宝贵时间) 看完了这个组件的名字 KeyboardAvoi...

4015
来自专栏腾讯社交用户体验设计

高清ICON SVG解决方案(下) - 腾讯ISUX

1731

扫码关注云+社区

领取腾讯云代金券