两个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哦!

版权声明:本文版权归作者所有,遵循 CC 4.0 BY-SA 许可协议, 转载请注明原文链接
https://www.cnblogs.com/scott-mr/p/8947639.html

如何正确使用const(常量),define(宏)-编程思维

前言 在开发中,也许我们会经常使用到宏定义,或者用const修饰一些数据类型,经常有开发者不知道怎么正确使用,导致项目中乱用宏定义与const修饰符。本篇主要介绍在开发中怎么正确使用const与define(宏定义) 当我们想定义全局共用的一些数据时,比如通知名字,动画时长等等,我们可以用宏、常量、变量: 宏: /

自定义转场详解(一)-编程思维

前言 本文是我学习了onevcat的这篇转场入门做的一点笔记。 今天我们来实现一个简单的自定义转场,我们先来看看这篇文章将要实现的一个效果图吧: 过程详解 热身准备 我们先创建一个工程,首先用storyboard快速的创建两个控制器,一个作为主控制器,叫ViewController,另外一个作为present出来的

Git从入门到速成-编程思维

什么是Git Git是Linux发明者Linus开发的一款新时代的版本控制系统,那什么是版本控制系统呢?怎么理解?网上一大堆详细的介绍,但是大多枯燥乏味,对于新手也很难理解,这里我只举几个例子。 熟悉编程的都知道,我们在软件开发中源代码其实是最重要的,那么对源代码的管理变得异常重要: 为了防止代码的丢失,肯定本地机器与

两个App之间调起通信-编程思维

前言 经常使用一些app的分享功能,比如点击QQ分享,就从app打开(跳转到)QQ,然后分享完之后又回到我们的app,那么这是怎样实现的呢? 假设有这么一个需求,由app1跳转到app2,当app2完成任务之后,要把app2中的某些信息传到app1中(假设我们自己的程序是app1) 过程 我们创建两个新的工程,一个叫T

用 notion 管理信用卡与花呗-编程思维

用 notion 管理信用卡与花呗 Notion原文,排版更佳 概述 不需要提醒功能和安卓用户可以忽略Scriptable和快捷指令 app的设置 Notion 建立信用卡表格,录入信用卡基本信息,自动计算还款日 使用 Scriptable iOS app 建立屏幕组件,调用 notion 私人 API 获取即将要还款

Android自定义ScrollView分段加载大文本数据到TextView-编程思维

以下内容为原创,转载时请注明链接地址:http://www.cnblogs.com/tiantianbyconan/p/3311658.html 这是我现在碰到的一个问题,如果需要在TextView中加载大文本的时候,比如几M的txt文件时,TextView载入的时候会出现卡死的现象,甚至会出现异常等待退出出现。 解决

android scrollviewwithstickheader - 编程思维

前言,一天在点外卖的时候,注意到饿了么列表页的滑动效果不错,但是觉得其中的手势滑动还是挺复杂的,正好又碰到了在熟悉Touch事件的理解当中,所以就抽空对着饿了么的列表页面尝试写写这个效果 1.先贴一个实现的效果图 逻辑是当外部的ScrollView没有滑到底部的时候,往上滑动的时候,是滑动外部的ScrollView,当