iOS开发-------滚动视图(UIScrollView)并实现循环滚动

       滚动视图(UIScrollView) 其实是一个能够实现存放大图片以及实现滚动的组件,比如有的图片很大,但是用UIImageView装不下,那么这个时候就需要UIScrollView了,如果说举例子的话,很多地方都用到了,比如页面滚动的广告视图,滚动的头条等,那么它是一种什么效果呢,相信看完图之后立马就会明白了,因为这个程序非常简单,重点在于如何应用  滚动视图(UIScrollView),以及能够实现自动滚动的同时实现循环滚动。


        该demo是比较早的时候写的,所以没有遵守MVC架构以及也没有屏幕适配,如果想达到比较好的效果,请适配4s或者5(s)的宽为320的模拟器


         iOS开发-------滚动视图(UIScrollView)并实现循环滚动_第1张图片


      如果说用到UIScrollView(滚动视图),首先必须要明白的就是两个属性,一个叫做ContentOffSet(当前可见视图的左上角 在 背后屏幕视图 的坐标,俗称偏移量),另一个叫做ContentSize(背景屏幕视图 的大小),它之所以能够滚动,不是因为他只有一个滚动的属性,而是他的背后视图更大,一个视图显示不过来,所以才会出现能够移动,要想移动或者滚动,必须要设置ContentSize,解释如图


iOS开发-------滚动视图(UIScrollView)并实现循环滚动_第2张图片



      接着说一下,能够实现循环滚动的实现方法,实际是在第一张 以及最后一张 放置一张图,第一张的图和倒数第二张图(目测的最后一张图)是一样的,倒数第一张的图和第二张图(目测的第一张图)是一样的,实现用下面的图解释,红色的框为ScrollView的边框

iOS开发-------滚动视图(UIScrollView)并实现循环滚动_第3张图片

       当滚到右边数第二张图片的时候,下一次做滚动的时候就是最右侧的一张,利用时间差,不用动画效果,立马跳到第二张,因为是一样的,所以人的眼睛是感觉不出来的,第一张的切换是一样的。也就是说,如果想实现三张图片的循环滚动的话,就需要5个视图来当做背后视图


首先需要在延展中定义几个组件,如下

UIScrollView:滚动视图的前端(用以展示视图)

UILabel:用于显示滚动视图下侧的测试文字(如:RunIntoLove1)

UIPageControl:用于展示当前的页数小圆点,比如下面的三个白色的小圆点

NSTimer:定时器,用于相隔时间段调用切换图片的方法

//
//  ViewController.m
//  ScrollView 实现无限滚动
//
//  Created by YueWen on 15/8/25.
//  Copyright (c) 2015年 YueWen. All rights reserved.
//

#import "ViewController.h"

#define COUNT 3//当前展示的视图的个数

@interface ViewController ()

@property(nonatomic,strong)UIScrollView * scrollView;//滚动视图
@property(nonatomic,strong)UILabel * label;//显示测试的视图
@property(nonatomic,strong)UIPageControl * pageControl;//页面控制器
@property(nonatomic,strong)NSMutableArray * items;//存取图片的数组
@property(nonatomic,strong)NSTimer * timer;//计时器

-(void)changeImage;//切换图片的方法
@end

接着需要在viewDidLoad中手动创建组件

首先加载在属性存储数据的数组items,每隔元素为一个字典,里面分别存图片的名字以及label显示的文字,因为比较复杂,so,打包成了一个方法,只需要在方法中调用即可,[self loadData],如下

/**
 *  加载存放label数据和图片的数组
 */
-(void)loadData
{
    self.items = [NSMutableArray array];
    
    
    
    //将最后一张添加到第一张的前方
    NSString * picName1 = [NSString stringWithFormat:@"%d.png",COUNT - 1];
    UIImage * image1 = [UIImage imageNamed:picName1];
    NSDictionary * dic1 = @{@"image":image1};
    [self.items addObject:dic1];
    
    for (int i = 0; i < COUNT; i++)
    {
        //获取图片
        NSString * picName = [NSString stringWithFormat:@"%d.png",i];
        UIImage  * image = [UIImage imageNamed:picName];
        //组建字典
        NSString * s = [NSString stringWithFormat:@"RunIntoLove %d",i];
        NSDictionary * dic = @{@"image":image,@"title":s};
        //加入数组
        [self.items addObject:dic];

    }
    
    //将第一张添加到最后一张的后方
    NSString * picName2 = [NSString stringWithFormat:@"%d.png",0];
    UIImage * image2 = [UIImage imageNamed:picName2];
    NSDictionary * dic2 = @{@"image":image2};
    [self.items addObject:dic2];
    
}

然后初始化主角,UISrcollView(滚动视图),最重要的属性在上面也已经提过了,相信也能看得懂,如下

    //初始化横向滚动视图
    self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 80, 320, 160)];//距离顶端80,宽为320,高为160
    self.scrollView.backgroundColor = [UIColor blackColor];//背景颜色,只为测试,没有实际意义,运行时会被图片盖住
    self.scrollView.delegate = self;//设置代理
    [self.scrollView setContentOffset:CGPointMake(320, 0)];//将起始点定义到第二张图
    [self.view addSubview:self.scrollView];//在父视图上添加

接着初始化下方的label

    //初始化显示文字的label
    self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 240, 320, 40)];
    self.label.text = [self.items[1] objectForKey:@"title"];
    self.label.textAlignment = NSTextAlignmentCenter;//文字居中
    self.label.backgroundColor = [UIColor blackColor];
    self.label.textColor = [UIColor whiteColor];
    self.label.alpha = 0.6;//透明度
    [self.view addSubview:self.label];

初始化第二主角,pageController(页码控制器):

    //初始化pageControl
    self.pageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, 210, 320, 40)];
    self.pageControl.numberOfPages = self.items.count - 2;//页码控制器的数量是 图片数 - 2(最前和最后的重复的)
    self.pageControl.currentPage = 0;//初始化当前的页码为0(与数组小标很类似,以0为开始)
    [self.view addSubview:self.pageControl];

然后需要配置一下ScrollView,也就是扩充一下背景视图了,楼主当时也打包了一个方法,如下

/**
 *  加载scollView的方法
 *  将self.items中的图片加到scollView中
 */
-(void)loadScollView
{
    for (int i = 0; i < self.items.count; i++)
    {
        UIImageView * view = [[UIImageView alloc] initWithFrame:CGRectMake(i * 320, 0, 320, 160)];
        view.image = [self.items[i] objectForKey:@"image"];
        [self.scrollView addSubview:view];
    }
}

但是不要忘记一个重要的属性,叫做ContentSize,如果不设置这个属性,是没有办法实现滚动的

[self.scrollView setContentSize:CGSizeMake(320 * self.items.count, 160)];//设置背景视图的大小,高为160,宽为图片张数 * 320(每张图片的宽度)
self.scrollView.pagingEnabled = YES;//有分页效果


因为创建好之后就需要自动滚动,定时器就需要在ViewDidLoad中创建好了

//初始化timer,用这个方法初始化计时器不需要手动启动计时器,如果用init需要手动启动
self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(changeImage) userInfo:nil repeats:YES];//表示每隔两秒调用一下 自身的 changeImage 方法,重复调用

下面是定时器的回调方法,意思就是让scrollView实现滚动效果

/**
 *  定时器调用的滑动图的方法
 */
-(void)changeImage
{
    //获得当先scrollView滚动到的点(俗称偏移量)
    CGFloat offSetX = self.scrollView.contentOffset.x;//获取当前滚动视图的contentOffSet的x值

    //让scrollView向右滚动一个屏幕宽的距离
    offSetX += self.scrollView.bounds.size.width;

    [self.scrollView setContentOffset:CGPointMake(offSetX, 0) animated:YES];//开始偏移并伴有动画效果

}

     如果做完上面的几部,滚动是可以实现了,但是滚动到最后一页的时候也就不会滚动了,毕竟跳跃这一步使用代码实现的,并且页码控制器也是不会发生变化的,那么首先来看一下页码控制器的控制吧,思路就是当页面滚动完毕之后,页码进行变化,所以需要控制scrollView的相关操作,如何呢,内部给开发者已经打包好了,是委托回调,因此在之前的viewDidLoad中设置代理,self.scrollView.delegate = self;

实现页码切换的时候自然是在以下方法中,相信注释也比较清楚了,但是方法不唯一,楼主只是用的其中一种

/**
 *  滚动视图滚动的时候的委托回调方法
 *
 *  @param scrollView 滚动的视图
 */
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    //获得偏移量
    CGPoint point = self.scrollView.contentOffset;
    //获得当前的最大x值(在可见区域内,最大的x轴上的值)
    CGFloat manX = self.scrollView.bounds.size.width * (self.items.count - 1);
    
    //如果当前点已经到了最前边的一张,即坐标为0,0
    if (point.x == 0)
    {
        CGFloat x = self.scrollView.bounds.size.width * (self.items.count - 2);
        [self.scrollView setContentOffset:CGPointMake(x , 0)];//立马跳到倒数第二张(因为最后一张是为了往后滚动做的铺垫视图)
    }
    
    //如果当前点已经达到最后一张图,即坐标为
    else if (point.x == manX)
    {
        [self.scrollView setContentOffset:CGPointMake(self.scrollView.bounds.size.width, 0)];//立马跳到整数第二张,第一张同理,利用人的视觉差
    }
    
    
    //设置pageControl的点数
    int pageNumber = [self imageIndexWithContentOffset:scrollView.contentOffset];//自定义方法,根据偏移量设置当前页码
    self.pageControl.currentPage = pageNumber;
    
    //改变标签
    int index = pageNumber + 1;
    self.label.text = [self.items[index] objectForKey:@"title"];
    
}

那么计算页码的方法会不会很难呢,一样简单,楼主的转变页码的时机是一整页全部展现在视图上的时候

/**
 *  获取当前属于第几页,用于改变pageControl
 *
 *  @param contentOffSet scrollView的左上角的点
 *
 *  @return 返回属于第几张
 */
-(int)imageIndexWithContentOffset:(CGPoint)contentOffSet
{
      return (contentOffSet.x - self.scrollView.bounds.size.width) / 320;
}

这么写基本代码就已经可以了,不过还是有点小bug的,自动跳转已经没有问题了,但是当手动干预的时候,计时器也会调用,所以操作不当的时候,页面会跳转得飞快,如果不想有这个小bug,需要进行如下操作

/**
 *  滚动视图将要开始拖动滚动的时候的委托回调方法(自动调用偏移的时候不会回调这个方法,需外力作用,下面依旧是)
 *
 *  @param scrollView 将要开始的滚动视图
 */
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    [self.timer invalidate];//取消计时器
    self.timer = nil;//避免野指针
}

既然停止了,那么手滑动结束后,就需要重启计时器

/**
 *  滚动视图拖动滚动结束时候的委托回调方法(手拖动离开屏幕的时候)
 *
 *  @param scrollView 停止的滚动视图
 *  @param decelerate 是否减速
 */
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    self.timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(changeImage) userInfo:nil repeats:YES];
}

至此,一个简单的循环滚动视图就完成了,滚动起来的时候就算手动干预,也不会出现较大的Bug了。


另一种方法就是通过位置不断的变换位置,在这里不做说明,可以去github下载一下楼主的小demo研究一下

objc版

https://github.com/YRunIntoLove/Carousel-objc


你可能感兴趣的:(iOS)