美文网首页ios 开发常用iOSiOS知多少
UIImage图片无限滚动的实现

UIImage图片无限滚动的实现

作者: tripleCC | 来源:发表于2015-06-30 22:31 被阅读1972次

    当app需要切换显示的图片少时,可以使用创建多个UIImageView,来实现多个图片切换显示;但是在图片较多时,这种做法显得很耗内存。所以以下总结了一下自己知道的几个方法与实现,并做了一个简易的封装。

    获取演示代码

    封装轮播控件的应用(图片从网络加载):

    采用三个UIImageView+UIScrollView

    • 在图片显示完全(endDecelerating)时,重新设置三个UIImageView的图片内容
    • 调整UIScrollView的偏移量,始终显示中间的UIImageView

    如有图片1、2、3、4、5,默认存放图片5、1、2,显示中间图片1:

    1. 向后滚动,显示图片2
    2. 图片显示完全时,重新设置UIImageView中的图片为图片1、2、3
    3. 设置UIScrollView的偏移量,使其显示中间的UIImageView,即图片2
    4. 向后滚动,显示图片3
    5. 图片显示完全时,重新设置UIImageView中的图片为图片2、3、4
    6. 设置UIScrollView的偏移量,使其显示中间的UIImageView,即图片3

    向前滚动同理。

    如下图所示:

    • 初始时显示图片1,然后向左滑动

      Snip20150531_26.png
    • 滑动完成时显示的是图片2

      Snip20150531_27.png
    • 在滑动完成时,修改UIImageView显示的内容如下图所示

      Snip20150531_28.png
    • 接着上一步,立即修改UIScrollView的偏移量,使其显示中间的UIImageView,即图片2

      Snip20150531_29.png
      如上,最终结果显示的都是最中间的UIImageView,看起来像是无限个UIImageView一样

    采用两个UIImageView+UIScrollView

    这个方法,和上面的方法原理是一样的。
    假设使用UIV1表示始终显示的UIImageView,使用UIV2表示备份的UIImageView

    • 首先,初始状态如下图所示,显示的是图片1(为了方便查看,我把UIImageView下移了,实际上和上面一排重合)


      初始状态
    • 这时候,向右边滚动(是滚,不是滑...),UIV2就立即显示图片2,这是,在屏幕可以看见图片1、2


      向右滚动
    • 当滚动完成时只能看见图片2,如下


      滚动完成
    • 这是,将UIV1的图片换成图片2,同时将UIScrollView的偏移量设置到中间的位置(这个过程很快,实际看不出来有修改和移动)


      修改图片
      修改偏移量
    • 向左滚动的情况


      左滚动
    • 立即将UIV2的frame修改至最左边的位置,并设置图片为0


      左滚动修改UIV2图片并移动
    • 修改完成后情况


      移动完成
    • 修改UIV1图片为图片0,并且设置UIScrollView偏移至中间位置


      修改UIV1图片
      最终显示结果

    实现整体代码如下,有一个小技巧,使用tag标识对应的image,可以使代码更精简:

    @interface TPCScrollViewByTwoImageView() <UIScrollViewDelegate>
    
    @property (weak, nonatomic) UIScrollView *scrollView;
    /**
     *  当前显示的view
     */
    @property (weak, nonatomic) UIImageView *currentView;
    
    /**
     *  备份的view(左右滑动时,显示的view)
     */
    @property (weak, nonatomic) UIImageView *backupView;
    @end
    
    @implementation TPCScrollViewByTwoImageView
    
    - (instancetype)initWithFrame:(CGRect)frame
    {
        if (self = [super initWithFrame:frame]) {
            [self setUp];
        }
        
        return self;
    }
    
    - (void)awakeFromNib
    {
        [self setUp];
    }
    
    - (void)setUp
    {
        // 创建需要的三个控件
        UIScrollView *scrollView = [[UIScrollView alloc] init];
        scrollView.pagingEnabled = YES;
        scrollView.bounces = NO;
        scrollView.delegate = self;
        scrollView.backgroundColor = [UIColor redColor];
        [self addSubview:scrollView];
        self.scrollView = scrollView;
        
        UIImageView *currentView = [[UIImageView alloc] init];
        [self.scrollView addSubview:currentView];
        self.currentView = currentView;
        
        UIImageView *backupView = [[UIImageView alloc] init];
        [self.scrollView addSubview:backupView];
        self.backupView = backupView;
        
        self.backgroundColor = [UIColor greenColor];
    }
    
    - (void)layoutSubviews
    {
        [super layoutSubviews];
        
        self.scrollView.frame = self.bounds;
        
        CGFloat imageViewW = self.bounds.size.width;
        CGFloat imageViewH = self.bounds.size.height;
        // 设置scrollView的内容大小
        self.scrollView.contentSize = CGSizeMake(imageViewW * 3, 0);
        
        // 设置imageView的frame
        self.currentView.frame = CGRectMake(imageViewW, 0, imageViewW, imageViewH);
        self.backupView.frame = CGRectMake(imageViewW * 2, 0, imageViewW, imageViewH);
    }
    
    - (void)setImages:(NSArray *)images
    {
        _images = images;
        
        // 设置默认图片
        self.currentView.image = images[0];
        self.backupView.image = images[1];
        
        // 设置tag为图片下标
        self.currentView.tag = 0;
        self.backupView.tag = 1;
        
        self.scrollView.contentOffset = CGPointMake(imageViewW, 0);
    }
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        CGFloat offsetX = scrollView.contentOffset.x;
        
        // 根据偏移量,设置backView的图片,并修改其图片下标
        if (offsetX < self.bounds.size.width) {
            self.backupView.frame = CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height);
            self.backupView.tag = (self.currentView.tag - 1 + self.images.count) % self.images.count;
            self.backupView.image = self.images[self.backupView.tag];
        } else if (offsetX > self.bounds.size.width) {
            self.backupView.frame = CGRectMake(self.bounds.size.width * 2, 0, self.bounds.size.width, self.bounds.size.height);
            self.backupView.tag = (self.currentView.tag + 1) % self.images.count;
            self.backupView.image = self.images[self.backupView.tag];
        }
    }
    
    - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
    {
        CGFloat offsetX = scrollView.contentOffset.x;
        
        // 停止时,设置偏移量为currentView所在位置
        scrollView.contentOffset = CGPointMake(self.bounds.size.width, 0);
        
        // 实际上没有换页,就返回
        if (offsetX < self.bounds.size.width * 1.5 && offsetX > self.bounds.size.width * 0.5) {
            return;
        }
        
        // 根据backView的image,来进行图片更换
        self.currentView.image = self.backupView.image;
        
        // 设置当前图片下标
        self.currentView.tag = self.backupView.tag;
    }
    

    使用UICollectionView

    因为UICollectionView有cell重用机制,所以只需要两个cell,即可完成上面的功能,内存压力也不会太大。

    • 设置UICollectionView的属性
    UICollectionViewFlowLayout *collectionViewLayout = [[UICollectionViewFlowLayout alloc] init];
       // 设置cell间距
        collectionViewLayout.minimumLineSpacing = 0;
      // 设置尺寸为view的大小
        collectionViewLayout.itemSize = self.bounds.size;
      // 设置为水平滑动
        collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
        
        UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:self.bounds collectionViewLayout:collectionViewLayout];
        
        collectionView.delegate = self;
        collectionView.dataSource = self;
       // 设置页切换允许
        collectionView.pagingEnabled = YES;
        collectionView.bounces = NO;
    
    • 对cell进行注册
      [collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"tpc"];
    
    • 然后根据外界传入的图片,设置数据源方法即可实现了
    - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
    {
        return 1;
    }
    
    - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
    {
        return self.images.count;
    }
    
    - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
    {
        UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"tpc" forIndexPath:indexPath];
        UIImageView *imageView = [[UIImageView alloc] initWithImage:self.images[indexPath.row]];
        imageView.frame = self.bounds;
        cell.backgroundView = imageView;
        
        return cell;
    }
    

    总结

    对于前面两种方法,我使用了UIImageView的tag来记录对应图片的下标,所以也省去了一个变量。

    相关文章

      网友评论

      • Scott_Mr:mark,tag设置的好巧妙
      • 43bd89865d8c:我以前都是用的第一种啊,没想到还有两种方法,受教了,谢谢作者
      • d9557f883fd8:用collectionview 不能无限滚动啊。。。。
        tripleCC:@tripleCC 可以设置成无限滚动的,有相应的实例,这里没有实现
        tripleCC:@zhnnnnn 嗯,是的,不行
      • 23Years:第一种方案怎么结合SDWebimage使用呢?我这样用的:[imageview sdsetwithurl..... ],图片切换的时候会出现闪烁问题。求教!
        23Years: @tripleCC 谢谢
        tripleCC:@23Years 试过了,不会啊。你直接用我github上面的试试看?

      本文标题:UIImage图片无限滚动的实现

      本文链接:https://www.haomeiwen.com/subject/njaeqttx.html