Objective-C的UIScrollView(滚动视图)学习笔记

UIScrollView - 滚动视图

UIScrollView可以用于显示多余一个屏幕的内容,超出屏幕范围的内容可以通过滑动或缩放进行查看。UIScrollView是几个UIKit类的超类,包括UITableView和UITextView。

UIScrollView对象(或者简单地说,滚动视图)的中心概念是它是一个其起源可以在内容视图上调整的视图。它将内容剪切到frame中,frame通常(但不一定)与应用程序的主窗口一致。滚动视图跟踪手指的运动,并相应地调整原点(自身bounds的原点,不是frame)。显示其内容的视图通过滚动视图根据新原点绘制其自身的该部分内容,该原点固定在内容视图中的一个偏移量上。滚动视图本身除了显示垂直和水平滚动指示器外,不进行任何绘图。滚动视图必须知道内容视图的大小,以便知道何时停止滚动。默认情况下当滚动超过内容的边界时,它会有一个“回弹”效果

滚动视图中管理显示内容的绘制对象需要平铺内容的子视图,这样视图就不会超过屏幕的大小。当用户在滚动视图中滚动时,此对象会根据需要添加和删除子视图。

因为滚动视图没有滚动条,所以滚动视图必须知道触摸是表示滚动的意图还是表示跟踪内容中的子视图的意图。为了做出这个判断,它通过启动一个定时器来临时拦截触事件,在定时器触发之前,观察触摸的手指是否有任何移动。如果计时器在没有显著位置变化的情况下触发,滚动视图将跟踪事件发送到内容视图的触摸子视图。如果用户在计时器执行之前将手指拖到足够远的地方,滚动视图将取消子视图中的任何跟踪并执行滚动自身。子类可以覆盖touchesShouldBegin:withEvent:inContentView:方法、pagingEnabled方法和touchesShouldCancelInContentView:方法(由滚动视图调用),以影响滚动视图处理滚动手势的方式

滚动视图还处理内容的缩放和平移。当用户做出捏合或张开手势时,滚动视图会调整内容的偏移量和比例。当手势结束时,管理内容视图的对象应根据需要更新内容的子视图。(注意,这个手势可以结束,手指也可以放下。)在手势执行过程中,滚动视图不会向子视图发送任何跟踪调用。

UIScrollView类的代理对象必须采用UIScrollViewDelegate协议。为了实现缩放和平移,委托必须同时实现viewForZoomingInScrollView:和scrollViewDidEndZooming:withView:atScale:方法,此外最大缩放比例(maximumZoomScale)和最小缩放比例(minimumZoomScale)必须不同

滚动视图的约束

将UIScrollView添加到故事板中,并设置UIScrollView相对于父视图的约束后,通常我们会得到如下错误:

截屏2022-09-13 10.45.30.png

因为UScrollView 的可滚动大小是根据其子视图的约束自动计算的。为了完全定义可滚动内容的宽度和高度,需要对可滚动内容相对于滚动视图的所有边缘(leading,trailing,top, bottom)进行约束。确保可以在滚动视图的子视图中从滚动视图的leading(或top)边缘开始,到trailing(或bottom)边缘跟踪一行连续连接的约束。

所以通常情况下,在IOS11.0及更高的版本中,可以根据contentLayoutGuide属性布局:

截屏2022-09-13 14.59.31.png

但为了兼容IOS11.0以下的版本,通常不使用contentLayoutGuide属性,而是添加一个视图充当UIScrollView的内容视图,然后在添加的内容视图上布局子视图:

截屏2022-09-13 15.23.42.png

重要的事情说两遍 : 如果滚动视图水平滚动,那么滚动视图的子视图的水平布局约束一定是连续的,以撑起水平方向可滚动内容的宽度;如果滚动视图垂直滚动,那么滚动视图的子视图的垂直布局约束一定是连续的,以撑起垂直方向可滚动内容的高度。

UIScrollView常用属性
@property(nonatomic)         CGSize                       contentSize;  

属性描述Scrollview中的可显示区域,可以设置ScrollView的滚动范围,假如有一个Scrollview,它的frame为(0,0,320,480),而它的contentSize为(320,960).也就是说,这个scrollview整个内容的大小为(320,960),要通过上下滑动Scrollview来查看(320,480)后的内容。

@property(nonatomic)         CGPoint                      contentOffset;

属性描述Scrollview当前显示区域顶点相对于frame顶点的偏移量,可以设置ScrollView当前滚动的位置,假如有一个Scrollview,它的frame为(0,0,320,480),而它的contentSize为(320,960),也就是说你拉到最下面,contentOffset就是(0 ,480),也就是y偏移了480。

@property(nonatomic)         UIEdgeInsets                 contentInset;

属性描述Scrollview中内容视图的原点与Scrollview本身原点的关系,这个属性可以在四周增加滚动范围,比如有一个Scrollview,它的frame为(0,0,320,480),内容视图的frame为(0,30,320,480),那么相对于Scrollview的contentInset则为(0, 30)。

@property(nonatomic)         BOOL                         bounces;

属性描述一个布尔值,只是滚动到内容边界时是否反弹。如果此属性的值为“YES”,则滚动视图在遇到内容边界时会反弹。视觉上的反弹表示滚动已到达内容的边缘。如果值为“NO”,则滚动将立即在内容边界处停止,而不反弹。默认值为“YES”。

@property(nonatomic)         BOOL                         alwaysBounceHorizontal; 

属性描述一个布尔值,用于确定当水平滚动到达内容视图的结尾时是否始终发生反弹。如果此属性设置为YES并且bounces为YES,则即使内容小于滚动视图的边界,也允许水平拖动。默认值为NO。

@property(nonatomic)         BOOL                         alwaysBounceVertical;

属性描述一个布尔值,用于确定垂直滚动到达内容结尾时是否始终发生反弹。如果此属性设置为YES并且bounces设置为YES,则即使内容小于滚动视图的边界,也允许垂直拖动。默认值为NO。

@property(nonatomic,getter=isScrollEnabled) BOOL          scrollEnabled;

属性描述决定是否启用滚动的布尔值。如果此属性的值为YES,则启用滚动,如果为NO,则禁用滚动。默认是YES。当滚动被禁用时,滚动视图不接受触摸事件,并将事件沿响应链向上转发。

@property(nonatomic,getter=isPagingEnabled) BOOL          pagingEnabled API_UNAVAILABLE(tvOS);

属性描述一个布尔值,用于确定是否为滚动视图启用分页。如果此属性的值为YES,则当用户滚动时,滚动视图将以滚动视图边界的倍数停止。默认值为NO。

@property(nonatomic)         BOOL             showsHorizontalScrollIndicator;

属性描述一个布尔值,用于控制水平滚动指示器是否可见。默认值是YES。跟踪正在进行的滚动时,指示器是可见的,跟踪滚动之后,指示器淡出。

@property(nonatomic)         BOOL             showsVerticalScrollIndicator;

属性描述一个布尔值,用于控制垂直滚动指示器是否可见。默认值是YES。跟踪正在进行的滚动时,指示器是可见的,跟踪滚动之后,指示器淡出。

@property(nonatomic)         UIScrollViewIndicatorStyle   indicatorStyle;

属性描述滚动指示器的样式。默认的样式是UIScrollViewIndicatorStyleDefault。

@property(nonatomic,readonly,getter=isDragging)     BOOL dragging;

属性描述一个布尔值,指示用户是否已开始滚动内容。此属性持有的值可能需要一些时间或滚动距离才能设置为YES。

@property(nonatomic,readonly,getter=isTracking)     BOOL tracking; 

属性描述返回用户是否已触摸内容以启动滚动。如果用户已经触摸了内容视图,但是可能还没有开始拖动它,那么这个属性的值是YES。

@property(nonatomic,readonly,getter=isDecelerating) BOOL decelerating;

属性描述是否正在减速,返回用户抬起手指后内容是否在滚动视图中移动。如果用户没有拖动内容,但仍在滚动,则返回值为YES。

@property(nonatomic,readonly,getter=isZooming)       BOOL zooming; 

属性描述一个布尔值,指示内容视图当前是放大还是缩小。如果用户正在进行缩放手势,则此属性的值为YES,否则为NO。

@property(nonatomic) CGFloat minimumZoomScale;

属性描述指定可应用于滚动视图内容的最小比例因子的浮点值。这个值决定了内容可以缩放的大小。默认值是1.0。

@property(nonatomic) CGFloat maximumZoomScale;

属性描述指定可应用于滚动视图内容的最大缩放因子的浮点值。这个值决定了内容可以缩放的大小。它必须大于最小缩放比例,以使用缩放功能。默认值是1.0。

@property(nonatomic,getter=isDirectionalLockEnabled) BOOL directionalLockEnabled;

属性描述确定是否在特定方向上禁用滚动,如果此属性为“YES”,并且用户开始沿一个常规方向(水平或垂直)拖动,则滚动视图将禁用沿另一个方向的滚动。如果拖动方向是对角线,则滚动将不会被锁定,用户可以在拖动完成之前向任何方向拖动。默认值为“NO”。

@property(nonatomic) BOOL delaysContentTouches;

属性描述一个布尔值,用于确定滚动视图是否延迟处理触控手势。如果这个属性的值是YES,滚动视图将延迟处理触摸手势,直到它能够确定目的是否是滚动。如果值为NO,滚动视图立即调用touchesShouldBegin:withEvent:inContentView:函数。默认值是YES。

@property(nonatomic)         UIScrollViewDecelerationRate decelerationRate API_AVAILABLE(ios(3.0));

属性描述一个浮点值,用于确定用户抬起手指后的减速率。应用程序可以使用UIScrollViewDecelerationRateNormal和UIScrollViewDecelerationRateFast常量作为合理减速率的参考。

@property(nonatomic) BOOL  scrollsToTop API_UNAVAILABLE(tvOS); 

属性描述一个布尔值,用于控制是否启用滚动到顶部手势。滚动到顶部的手势是点击状态栏。当用户做这个动作时,系统会要求最靠近状态栏的滚动视图滚动到最上面。如果那个滚动视图将scrollsToTop设置为NO,那么它的委托将从scrollViewShouldScrollToTop:返回NO,或者内容已经在顶部,则什么也不会发生。

在滚动视图滚动到内容视图的顶部之后,它向委托发送一个scrollViewDidScrollToTop:消息。scrollsToTop的默认值是YES。

@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior API_AVAILABLE(ios(11.0),tvos(11.0));

属性描述 :用于确定调整的内容偏移的行为。此属性指定如何使用安全区域插入来修改滚动视图的内容区域。此属性的默认值为UIScrollViewContentInsetAdjustmentAutomatic

  • UIScrollViewContentInsetAdjustmentBehavior提供的枚举值:
typedef NS_ENUM(NSInteger, UIScrollViewContentInsetAdjustmentBehavior) {
    //自动调整滚动视图插图。当滚动视图是当前由导航或标签栏控制器显示的视图控制器的内容视图时,内容总是垂直调整。
    //如果滚动视图是水平可滚动的,则当存在非零值安全区域插入时,也会调整水平内容偏移。
    UIScrollViewContentInsetAdjustmentAutomatic, 
    //仅在可滚动方向调整插入。当垂直内容大小大于滚动视图本身的高度时,顶部和底部的插入会包括安全区域插入值。
    //当alwaysBounceVertical属性为YES时,也会调整顶部和底部插入。
    //类似地,当水平内容大小大于滚动视图的宽度时,左侧和右侧的插入会包括安全区域插入值。
    UIScrollViewContentInsetAdjustmentScrollableAxes, 
    //不要调整滚动视图的插入。
    UIScrollViewContentInsetAdjustmentNever,
    //始终在内容调整中包含安全区域的插入。
    UIScrollViewContentInsetAdjustmentAlways,
} API_AVAILABLE(ios(11.0),tvos(11.0));

通常滚动视图(包括其子类,如UICollectionView)因为该属性的默认值,会自动调整内容的插入以适应安全区域,如图:

截屏2023-01-18 15.21.33.png

如果不需要这种对内容插入的调整,可以设置该属性值为UIScrollViewContentInsetAdjustmentNever,如果设置该属性值未生效,则需要检查导航栏是否是透明的,如果导航栏不是透明的,则需要检查滚动视图(包括其子类,如UICollectionView)所在的视图控制器的extendedLayoutIncludesOpaqueBars属性是否设置为YES,导航栏透明的情况下,则不需要检查extendedLayoutIncludesOpaqueBars属性。设置完成后,如图:

截屏2023-01-18 15.59.02.png
@property(nullable,nonatomic,weak) id<UIScrollViewDelegate>        delegate;

属性描述滚动视图对象的代理,代理必须采用UIScrollViewDelegate协议。

UIScrollViewDelegate提供的函数

- (void)scrollViewDidScroll:(UIScrollView *)scrollView;

函数描述contentOffset内容偏移量改变时调用,委托通常实现此方法以从scrollView获取内容偏移量的更改,并绘制内容视图的受影响部分。

参数 :

scrollView:发生滚动的滚动视图对象。

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;

函数描述开始滚动时调用(可能需要一些时间或距离才能调用),在较短距离内发生滚动的话代理可能无法接收此消息。

参数 :

scrollView:即将滚动内容视图的滚动视图对象。

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset API_AVAILABLE(ios(5.0));

函数描述将要结束滚动时调用,应用程序可以更改targetContentOffset参数的值,以调整scrollview完成其滚动动画的位置。

参数 :

scrollView : 用户结束触摸的滚动视图对象。

velocity : 释放触摸时滚动视图的速度(以点为单位)。

targetContentOffset : 滚动操作减速到停止时的预期偏移量。

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;

函数描述结束滚动时调用

参数 :

scrollView:完成内容视图滚动的滚动视图对象。

decelerate:设为“YES”,如果在滚动操作期间的触摸手势之后,滚动移动将继续,但会减速。如果该值为“NO”,则触摸后滚动将立即停止。

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView;

函数描述开始减速滚动时调用,在滚动操作期间,当用户的手指离开触摸时,滚动视图调用此方法;之后,滚动视图将继续移动一小段距离。

参数 :

scrollView:正在减慢内容视图滚动速度的滚动视图对象。

- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView;

函数描述如果要点击状态栏滚动到顶部,请返回“YES”。如果没有定义,则默认YES。要使滚动到顶部手势(点击状态栏)生效,UIScrollView的scrollsToTop属性必须设置为YES。

参数:

scrollView :请求此信息的滚动视图对象。

返回值:

“YES”允许滚动到内容的顶部,“NO”禁止。

- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView;

函数描述通知代理滚动视图通过点击状态栏滚动到内容的顶部。滚动视图在滚动到内容顶部时发送此消息。如果内容的顶部已经显示,它可能会立即调用它。要使滚动到顶部手势(点击状态栏)生效,UIScrollView的scrollsToTop属性必须设置为YES。滑动到顶部不会执行。

参数 :

scrollView:执行滚动操作的滚动视图对象。

手势缩放

1.设置UIScrollView的id<UISCrollViewDelegate> delegate代理对象

2.设置minimumZoomScale :缩小的最小比例

3.设置maximumZoomScale :放大的最大比例

4.让代理对象实现viewForZoomingInScrollView:(UIScrollView *)scrollView方法,返回需要缩放的视图控件

与缩放相关的常用UIScrollViewDelegate函数
- (void)scrollViewDidZoom:(UIScrollView *)scrollView API_AVAILABLE(ios(3.2));

函数描述正在缩放的时候调用

参数 :

scrollView : 缩放因子已更改的滚动视图对象。

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UIView *)view atScale:(CGFloat)scale;

函数描述缩放完毕的时候调用

参数 :

scrollView : 显示内容视图的滚动视图对象。

view : 表示需要缩放的内容视图的那部分的视图对象。

scale : 用于缩放的比例因子;这个值必须在UIScrollView属性maximumZoomScale和minimumZoomScale所建立的限制之间。

分页效果

设置pagingEnabled = YES即可,UIScrollView会被分割成多个独立页面,用户的滚动体验则变成了页面翻转,一般会配合UIPageControl增强分页效果。

UIPageControl - 分页控件

一种显示一系列水平点的控件,每个点对应于应用程序文档或其他数据模型实体中的一页。当用户点击分页控件以移动到下一页或上一页时,该控件将发送UIControlEventValueChanged事件以供代理处理。然后代理可以计算currentPage属性以确定要显示的页面。分页控件在任何方向上都只前进一个页面。当前浏览的页面由一个白点表示。根据设备的不同,在裁剪之前,屏幕上会显示一定数量的点。

UIPageControl的常用属性
@property(nonatomic) NSInteger numberOfPages;

属性描述调用方显示的页数(以点表示)。属性的值是分页控件显示为点的页数。默认值为0。

@property(nonatomic) NSInteger currentPage;

属性描述当前页,由调用方显示为一个白点。属性值是一个整数,指定显示的当前页减去1,因此值为0(默认值)表示第一页,超出可能范围的值固定为0或numberOfPages减1。分页控件将当前页显示为白点。

@property(nonatomic) BOOL hidesForSinglePage;

属性描述一个布尔值,用于控制只有一个页面时控件是否隐藏。当只有一个页面时,为YES赋值以隐藏分页面控件,如果只有一个页面,则分配NO(默认值)来显示页面控件。

@property(nullable, nonatomic,strong) UIColor *pageIndicatorTintColor API_AVAILABLE(ios(6.0)) UI_APPEARANCE_SELECTOR;

属性描述用于页面指示器的颜色(即分页的圆点标记),页面指示点的默认颜色为半透明白色。页面指示点用于屏幕上不可见的所有页面。为此属性分配新值不会自动更改currentPageIndicatorTintColor属性中的颜色,因为这两个属性的值不会自动从另一个属性派生。两个属性都必须单独指定。类似地,没有alpha应用于此属性。建议(但不是必需)为此参数指定的颜色包含一些透明度,即alpha值应小于1.0。

@property(nullable, nonatomic,strong) UIColor *currentPageIndicatorTintColor API_AVAILABLE(ios(6.0)) UI_APPEARANCE_SELECTOR;

属性描述用于当前页指示器的颜色。默认颜色是当前页指示点的不透明白色。当前页面指示点用于指示当前可见的页面。为该属性指定新值不会自动更改PageIndicatorIntColor属性中的颜色,因为这两个属性的值不会自动从另一个属性派生。两个属性都必须单独指定。

练习代码

素材是这样的 :

屏幕快照 2019-02-13 上午1.40.17.png

代码示例 :

//引入ViewController头文件
#import "ViewController.h"
//声明为UIScrollView的代理类
@interface ViewController ()<UIScrollViewDelegate>
//分页视图控件
@property(nonatomic,strong) UIPageControl *pageControl;
//定时器
@property(nonatomic,strong) NSTimer *timer;
@end

@implementation ViewController
//视图生命周期的方法,加载视图时调用
- (void)viewDidLoad {
    [super viewDidLoad];
    //初始化UIScrollWiew大小为屏幕大小
    UIScrollView *scrollView = [[UIScrollView alloc]initWithFrame:self.view.frame];
    //设置滚动的范围
    scrollView.contentSize = CGSizeMake(CGRectGetWidth(self.view.frame) * 8, CGRectGetHeight(self.view.frame));
    //设置分页效果
    scrollView.pagingEnabled = YES;
    //水平滚动条隐藏
    scrollView.showsHorizontalScrollIndicator = NO;
    //声明图片名称
    NSString *imageName = nil;
    //声明图片
    UIImage *image = nil;
    
    for (int i = 0; i < 8; i++) {
        //为图片名称赋值
        imageName = [NSString stringWithFormat:@"0%d",i + 1];
        //初始化图片
        image= [UIImage imageNamed:imageName];
        //初始化图片视图,x轴的坐标在原点随着每次循环增加一个屏幕的宽度,y轴始终为0,宽高为屏幕的宽高
        UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(CGRectGetWidth(self.view.frame) * i, 0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame))];
        //为图片视图设置图片
        [imageView setImage:image];
        //为每个图片视图设置标签
        imageView.tag = 100 + I;
        //将图片视图添加到滚动视图
        [scrollView addSubview:imageView];
    }
    //viewWithTag的作用就是根据tag属性获取到对应的view、imageview、label等等。
    //获取第一张图片视图
    UIImageView *firstImageView = [scrollView viewWithTag:100];
    //为滚动视图最后面加一个视图,它和第一个视图一样,到这里实际已经有了9张图片视图
    UIImageView *lastImageView = [[UIImageView alloc]initWithFrame:CGRectMake(CGRectGetWidth(self.view.frame) * 8, 0, CGRectGetWidth(self.view.frame), CGRectGetHeight(self.view.frame))];
    //将第一张图片视图的图片赋值给最后一张图片视图的图片属性
    lastImageView.image = firstImageView.image;
    //将最后一张图片视图添加到滚动视图上
    [scrollView addSubview:lastImageView];
    //将滚动视图添加到控制器视图
    [self.view addSubview:scrollView];
    //为滚动视图添加标签
    scrollView.tag = 100;
    //初始化分页视图控件,并设置好位置
    self.pageControl = [[UIPageControl alloc]initWithFrame:CGRectMake(0, CGRectGetHeight(self.view.frame) - 50, CGRectGetWidth(self.view.frame), 50)];
    //设置分为多少页
    self.pageControl.numberOfPages = 8;
    //设置当前所在页
    self.pageControl.currentPage = 0;
    //设置页面指示器的颜色(即分页的圆点标记)
    self.pageControl.pageIndicatorTintColor = [UIColor redColor];
    //设置当前所在页面指示器的颜色
    self.pageControl.currentPageIndicatorTintColor = [UIColor greenColor];
    //将分页视图控件添加到控制器视图
    [self.view addSubview: self.pageControl];
    //初始化定时器,scheduledTimerWithTimeInterval设定间隔时间1.0秒,target指定发送消息给哪个对象,selector指定要调用的方法名,userInfo可以给消息发送参数,repeats设定是否重复
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(circularDisplayView) userInfo:nil repeats:YES];
    //将本类赋值给scrollView的代理属性
    scrollView.delegate = self;
}
#pragma mark - 滚动视图的代理方法
//视图被拖拽时调用,在此方法中会暂停控制器,停止图片循环展示
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
    //setFireDate:设置定时器的启动时间
    //[NSDate distantFuture]:遥远的未来
    [self.timer setFireDate:[NSDate distantFuture]];
}
//视图静止时(没有被拖拽)时调用,在此方法中会开启定时器,让图片循环展示
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    //延迟2秒在开启定时器
    //[NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]] 返回值为现在时刻开始在过1.5秒的时刻
    [self.timer setFireDate:[NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]]];
}
//定时器的回调方法,切换页面
-(void)circularDisplayView{
    //得到scrollView
    UIScrollView *scrollView = [self.view viewWithTag:100];
    //通过改变contentOffset来切换滚动视图的子界面
    CGFloat offset_x = scrollView.contentOffset.x;
    //每次切换一个屏幕
    offset_x +=CGRectGetWidth(self.view.frame);
    //当额外添加的最后一张图片视图开始滚动时,将偏移量改为第一个图片视图的位置
    if(offset_x > CGRectGetWidth(self.view.frame) * 8){
        scrollView.contentOffset = CGPointMake(0, 0);
    }
    //当显示的是额外添加的最后一张图片视图时,将分页控件的当前分页指示器重新开始设置,否则正常显示
    if(offset_x == CGRectGetWidth(self.view.frame) * 8){
        self.pageControl.currentPage = 0;
    }else{
        self.pageControl.currentPage = offset_x / CGRectGetWidth(self.view.frame);
    }
    //设置最终偏移量
    CGPoint finalPoint = CGPointMake(offset_x, 0);
    // 切换视图时带动画效果
    //当额外添加的最后一张图片视图开始滚动时,即相当于第一张图片开始滚动,直接将偏移量设置到第二张图片的位置
    if (offset_x > CGRectGetWidth(self.view.frame) * 8) {
        self.pageControl.currentPage = 1;
        [scrollView setContentOffset:CGPointMake(CGRectGetWidth(self.view.frame), 0) animated:YES];
    }else{
        [scrollView setContentOffset:finalPoint animated:YES];
    }
}


@end

运行效果:

Untitled.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 一、简介 <<继承关系:UIScrollView --> UIView-->UIResponder-->NSObj...
    无邪8阅读 1,846评论 0 0
  • 掌握 UIScrollView的常见属性 UIScrollView的常用代理方法 UIScrollView的缩放 ...
    JonesCxy阅读 2,713评论 1 12
  • - 什么是UISCrollView + 当手机屏幕需要展示的内容较多超出一个屏幕时,用户可以通过滚动手势来查看屏幕...
    Alan张洋阅读 22,031评论 2 19
  • 在iOS中,滚动视图UIScrollView用于查看大于屏幕的内容。Scroll View有两个主要目的: 让用户...
    pro648阅读 38,080评论 4 37
  • 我不知道我要多坚强才能接受你已经慢慢的开始离开。我知道我在自欺欺人你还陪着我,我知道谁都瞧不起我。 今天坚强负分晚...
    再见CLT阅读 186评论 0 0