2021-07-19

iOS——轮播图的几种实现方式

1、UIScrollView + UIImageView

  • 1.1 使用比图片数多2个的UIImageView实现
  • 1.2 使用3个UIImageView实现
  • 1.3 实现2个UIImageView实现

使用比图片数多两个的UIImageView方式实现

思路 : 创建 比图片数多 2个UIImageView ,最一个ImageView显示第一张图片,第一个ImageView显示最后一张图片,从第二个到倒数第二个ImageView依次显示第一张到最后一张图片 当滚动到最后一张图片, 即 :原始 图片顺序为 1,2,3,4,5,6 ,则八个ImageView 显示图片的顺序为6,1,2,3,4,5,6,1,依次把ImageView添加到UIScrollView上,最开始显现第二个ImageView,当滚动到第一个 ImageView的时候,控制UIScrollView滚动到倒数第二个 ImageView,滚动到最后一个 ImageView的时候,控制UIScrollView滚到第二个ImageView ,只有 中间的 是正式用来显示的.两头的用来实现一个视差效果。

使用3个UIImageView实现

思路: 方法1.1 的进化版,方法1.1 在有大量图片的情况下,需要创建大量的ImageView 明显不合适,所以把中间的一个ImageView片来代替 1.1中的大量中间图片,即只有中间一张图片是用来正式显示图片的,每次滚动结束都需要滚回到 中间图片位置,两边的ImageView 用来实现视差效果。一共有3个imageView,初始化状态看到的是位于3个imageView中间的第一张图片,此时scrollView的contentOffsetX等于width,然后左边是最后一张图片,右边是第二张图片。如果用户向左滑动显示第二张图片,当第二张图片完全显示出来后,此时scrollView的contentOffsetX等于2*width,我们要做的第一件事就是将用户移动的位置进行复位,也就是让scrollView的contentOffsetX变回width,然后重新设置每个imageView应该显示的图片,因为代码执行速度很快,所以你意识不到它的切换。这样左边的imageView显示的是当前图片的上一张图片,右边的imageView显示的是当前图片的下一张图片

在这里插入图片描述

使用2个UIImageView实现

思路: UIScrollView 的 contentSize 依然 为 三倍 屏幕宽度,创建两个 ImageView 添加到 UIScrollView 上, 没有滚动时,两个imageView重叠,上面的ImageView显示当前图片,发生滚动的时候,判断滚动方向,把底下的ImageView移到 左边或者右边,滚动结束,UIScrollView 滚动回中间位置

轮播实现的步骤:

层级结构
最底层是一个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];
    }

2、UICollectionView (UICollectionView也是继承自UIScrollView)

  • 2.1 使用UICollectionView 的复用特性,复制多份图片作为数据源,从中间开始显示
  • 2.2 在 第一张图片前加 上最后一张图片,在最后一张图片前加上第一张图片

使用UICollectionView 的复用特性,复制多份图片作为数据源,从中间开始显示

思路: 复制多份需要显示的图片作为UICollectionView数据源,开始的时候就把scrollView 滚动到中间那份数据的开头位置,因为复制的多份数据,所以滚动的时候就会无限轮播的效果

在第一张图片前加上最后一张图片,在最后一张图片前加上第一张图片

思路: 和1.1 方法类似,只是使用 UICollectionView 的话因为UICollectionView自身的复用机制,所以没有 1.1方法 创建大量 ImageView的性能问题,同样的,在首尾 分别加上最后和第一张图片,当滚动到这两个位置的时候,控制 scrollView滚动到中间显示区的对应位置

CollectionCell 和tableViewCell 用法不太一样, CollectionCell 需要注册, 告诉系统这种标识对应的cell是什么类型的cell, 如果缓冲池中没有, 自动创建这种类型的cell

UICollectionView和UITableview 均继承自UIScrollerView

两个视图控件属性
一个就是UICollectionView用于显示轮播图 需要在其中加入自定义的CollectionViewCell
一个是UIPageControl用于进行翻页显示

两个定义属性
一个定时器用于设置图片定时的切换
一个是用于网络请求完成之后进行CollectionView UI数据的刷新

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

定义一个imageArr用来接收外界传进来的数组(可以是图片,也可以是网络图片路径,可以图片和路径混合)
定义一个images用来存储图片(只装图片).判断外界传进来的数组,如果是图片,直接添加到images,如果是连接,先添加一张默认的占位图
定义一个imageDic用来缓存图片的字典,key为图片URL
定义一个operationDic用来保存下载操作的字典,key为图片URL
图片缓存策略(SDWebImage的思路)
下载图片,先从缓存中取,如果有,则替换之前的占位图片,如果没有,去沙盒中取,如果有,替换占位图片,并添加到缓存中,如果没有,开启异步线程下载

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

通过block:

定义一个block给外界
打开currentImageView的用户交互
给currentImageView添加一个点击手势
在点击手势响应方法里面调用block,并传入图片所在的索引

通过代理:

定义一个协议方法,设置一个代理属性
打开currentImageView的用户交互
给currentImageView添加一个点击手势
在点击手势响应方法里面用代理调用协议方法,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值