目录主要分为以下几个样式:
常用、会用、了解
目录
- UIScrollViewDelegate
-
滚动&&拖动
- scrollViewDidScroll:
- scrollViewWillEndDragging:withVelocity:targetContentOffset:
- scrollViewDidEndDragging:willDecelerate:
- scrollViewShouldScrollToTop:
- scrollViewDidScrollToTop:
- scrollViewWillBeginDecelerating:
- scrollViewDidEndDecelerating:
- 缩放管理
- viewForZoomingInScrollView:
- scrollViewWillBeginZooming:withView:
- scrollViewDidEndZooming:withView:atScale:
- scrollViewDidZoom:
- UIScrollView
-
滚动动画
- scrollViewDidEndScrollingAnimation:
-
adjustedContentInset更改
- scrollViewDidChangeAdjustedContentInset:
- UIScrollView
-
内容大小&&偏移量
- contentSize
- contentOffset
- setContentOffset:animated:
-
内容嵌入
- adjustedContentInset(iOS11+)
- contentInset(iOS11-)
- contentInsetAdjustmentBehavior
- adjustedContentInsetDidChange
-
配置UIScrollView
- scrollEnabled
- directionalLockEnabled
- pagingEnabled
- scrollsToTop
- bounces
- alwaysBounceVertical
- alwaysBounceHorizontal
-
获得滚动状态
- tracking
- dragging
- decelerating
- decelerationRate
-
滚动条 && 刷新控件
- indicatorStyle
- scrollIndicatorInsets
- showsHorizontalScrollIndicator
- showsVerticalScrollIndicator
- flashScrollIndicators
- refreshControl
-
滚动到指定区域
- scrollRectToVisible:animated:
-
触摸管理
- touchesShouldBegin:withEvent:inContentView:
- touchesShouldCancelInContentView:
- canCancelContentTouches
- delaysContentTouches
-
缩放和移动
- panGestureRecognizer
- pinchGestureRecognizer
- zoomToRect:animated:
- setZoomScale:animated:
- maximumZoomScale
- minimumZoomScale
- zoomBouncing
- zooming
- bouncesZoom
-
键盘管理
- keyboardDismissMode
-
索引
- indexDisplayMode
- 参考资料
UIScrollViewDelegate
通过协议方法响应滚动、缩放、滚动内容的减速和滚动动画等操作。
滚动&&拖动
-
scrollViewDidScroll:
视图滚动(contentOffset改变)时触发
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
这个方法在任何方式触发 contentOffset 变化的时候都会被调用(包括用户拖动,减速过程,直接通过代码设置等),可以用于监控 contentOffset 的变化,并根据当前的 contentOffset 对其他 view 做出随动调整。
-
scrollViewWillEndDragging:withVelocity:targetContentOffset:
用户手指离开屏幕(滚动完成)时触发
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset;
velocity
表示惯性速度、targetContentOffset
表示将会在哪里完全静止。
当velocity
不为CGPointZero
时,UIScrollview
会以 velocity
为初速度,减速直到 targetContentOffset
。
需要注意的是:
这里的 targetContentOffset 是个指针,没错,你可以改变减速运动的目的地
-
scrollViewDidEndDragging:willDecelerate:
用户手指离开屏幕(滚动完成)时触发
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate;
decelerate
为bool值
、表示是否有继续滚动的减速过程。
这个值、取决于WillEndDragging
方法中velocity
是否为CGPointZero
。
需要注意的是:
- 在
didEndDragging
之后,如果有减速过程,UIScrollview
的dragging
属性 并不会立即置为NO
,而是要等到减速结束之后。 - 通过代码的方式(
- setContentOffset:animated:
)并不会触发。
-
scrollViewShouldScrollToTop:
当触摸状态栏时、是否允许UIScrollView滚动到顶部。默认返回YES
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView;
这个效果依赖scrollsToTop
属性、当然他默认也是YES
。
-
scrollViewDidScrollToTop:
UIScrollView滚动到顶部之后的回调
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView;
-
scrollViewWillBeginDecelerating:
UIScrollView开始自动减速时触发
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView;
-
scrollViewDidEndDecelerating:
UIScrollView减速结束时触发
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
这里有一种特殊情况需要注意:
当一次减速动画尚未结束的时候再次拖动 UIScrollView
,didEndDecelerating
先不会被调用,并且这时 UIScrollView
的 dragging
和 decelerating
属性都是 YES
。
- 新的拖动如果有加速度
那么willBeginDecelerating
会再一次被调用,然后才是didEndDecelerating
- 新的拖动如果没有加速度
虽然willBeginDecelerating
不会被调用,但前一次留下的didEndDecelerating
会被调用。
缩放管理
-
viewForZoomingInScrollView:
在缩放手势发生时、返回需要变化的View。不允许缩放可以返回nil
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;
你还需要其他属性进行配合才能实现缩放功能
Demo的话可以看一个帖子《iOS开发UI篇—UIScrollView控件实现图片缩放功能》
此外有几点需要注意:
- 返回的
View
并不必须为UIScrollView
的子视图 - 系统会根据手势修改该
View
的transform
进而完成缩放。
-
scrollViewWillBeginZooming:withView:
缩放开始时触发
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView
withView:(UIView *)view;
此时View的状态为缩放之前
-
scrollViewDidEndZooming:withView:atScale:
缩放完成时触发
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView
withView:(UIView *)view
atScale:(CGFloat)scale;
此时View的状态为缩放之后
-
scrollViewDidZoom:
发生缩放时触发
- (void)scrollViewDidZoom:(UIScrollView *)scrollView;
实时触发、和scrollViewDidScroll
一样
滚动动画
-
scrollViewDidEndScrollingAnimation:
滚动结束时触发
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView;
在通过代码的方式(- setContentOffset:animated:
)令UIScrollView
滚动结束时触发。
adjustedContentInset更改
-
scrollViewDidChangeAdjustedContentInset:
adjustedContentInset
属性改变时触发
- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView;
UIScrollView
一个允许对其包含视图进行滚动、缩放操作的视图
继承关系
UIScrollView是UITableView和UITextView的父类。
响应原理
当用户手指触摸UIScrollView及其子类时,其属性 isTracking 设置为YES,同时内部会开启一个NSTimer, 在timer的一个极短的时间周期内,如果手指发生了较大距离的移动,UIScrollView接收这个事件开始滚动到相应的位置, isTracking 设置为NO。 如果手指没有发生较大距离的移动,而touch位置正好位于子视图上,并且其子视图接受touch事件,那么就触发子视图的touch事件。
我们可以通过以下三个方法来影响它处理滚动手势的方式:
touchesShouldBegin:withEvent:inContentView:
、 pagingEnabled
、 touchesShouldCancelInContentView:
滚动时会触发layoutSubviews
UIScrollView还可以处理内容的缩放和平移:
- 当手势正在进行时,滚动视图不会向子视图发送任何跟踪调用。
- UIScrollView类可以有一个必须采用UIScrollViewDelegate协议的委托。要使缩放和平移工作,委托必须实现
viewForZoomingInScrollView:
和scrollViewDidEndZooming:withView:atScale:
- 最大变焦比例尺和最小变焦比例尺必须不同。
状态恢复
如果你实现了restorationIdentifier
、那么它会试图在应用程序启动之间保存与滚动相关的信息(zoomScale、contentInset和contentOffset)。
内容大小&&偏移量
-
contentSize
可以滚动的内容大小
@property(nonatomic) CGSize contentSize;
-
contentOffset
偏移量
@property(nonatomic) CGPoint contentOffset;
-
-setContentOffset:animated:
允许通过动画的方式设置
contentOffset
- (void)setContentOffset:(CGPoint)contentOffset
animated:(BOOL)animated;
内容嵌入
主要控制内容展示的范围、frame(0.0)的位置。
十分重要、因为iOS11之后安全区域以及adjustedContentInset
的引入。
-
adjustedContentInset
允许内容到边缘的距离(决定iOS11以上)
@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset;
在iOS11中替代了contentInset
起到最终决定作用、其值和contentInset
以及contentInsetAdjustmentBehavior
有关
以MJRefresh内的代码为例
- (UIEdgeInsets)mj_inset
{
#ifdef __IPHONE_11_0
if (respondsToAdjustedContentInset_) {
return self.adjustedContentInset;
}
#endif
return self.contentInset;
}
contentInsetAdjustmentBehavior
的类型决定是否在调整中包含安全区域、具体的数值可以参考《本文》
-
contentInset
允许内容到边缘的距离(决定iOS11以下)
@property(nonatomic) UIEdgeInsets contentInset;
使用此属性可扩展内容和内容视图边缘之间的空间。
-
contentInsetAdjustmentBehavior
决定调整后的
adjustedContentInset
具体值、默认UIScrollViewContentInsetAdjustmentAutomatic
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;
UIScrollViewContentInsetAdjustmentAutomatic:如果scrollview在一个automaticallyAdjustsScrollViewInsets = YES的controller上,并且这个Controller包含在一个navigation controller中,这种情况下会设置在top & bottom上 adjustedContentInset = safeAreaInset + contentInset不管是否滚动。其他情况下与UIScrollViewContentInsetAdjustmentScrollableAxes相同
UIScrollViewContentInsetAdjustmentScrollableAxes: 在可滚动方向上adjustedContentInset = safeAreaInset + contentInset,在不可滚动方向上adjustedContentInset = contentInset;依赖于scrollEnabled和alwaysBounceHorizontal / vertical = YES,scrollEnabled默认为yes,所以大多数情况下,计算方式还是adjustedContentInset = safeAreaInset + contentInset
UIScrollViewContentInsetAdjustmentNever: adjustedContentInset = contentInset
UIScrollViewContentInsetAdjustmentAlways: adjustedContentInset = safeAreaInset + contentInset
通常、如果我们想要屏蔽系统的安全区域(比如我们想满屏显示、甚至被状态栏遮挡)
if (@available(iOS 11.0, *)) {
self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
-
adjustedContentInsetDidChange
adjustedContentInset
值改变时触发的放啊
- (void)adjustedContentInsetDidChange;
需要注意UIScrollViewDelegate
的scrollViewDidChangeAdjustedContentInset
也能捕获这个动作、当然、他是给外界用的。
配置UIScrollView
能否滚动、滚动方向、分页、回归顶部、弹簧效果等
-
scrollEnabled
是否能够滚动
@property(nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
-
directionalLockEnabled
滚动时是否锁定另一坐标轴上的滚动
@property(nonatomic, getter=isDirectionalLockEnabled) BOOL directionalLockEnabled;
如果为YES、那么在同一次操作中你将只能在一个坐标轴上滚动。
但是官方文档上指出、如果对角(45°)滑动这个属性将失效。
有兴趣可以参阅《Stackoverflow》中的讨论。
-
pagingEnabled
其否启用分页滑动
@property(nonatomic, getter=isPagingEnabled) BOOL pagingEnabled;
每页的宽/高度为scrollview的bounds
-
scrollsToTop
是否允许(当触摸状态栏时)滚动到顶部、默认YES
@property(nonatomic) BOOL scrollsToTop;
对于一个scrollsToTop
为YES的UIScrollView
、你可以通过scrollViewShouldScrollToTop
代理的返回值控制具体是否可以回归顶部。
需要注意的是如果有多个UIScrollView
该值为YES、那么他们都将失效。
-
bounces
弹簧效果、默认YES
@property(nonatomic) BOOL bounces;
在滚动超出内容后、会反弹回来。
-
alwaysBounceVertical
垂直方向否有弹簧效果
@property(nonatomic) BOOL alwaysBounceVertical;
-
alwaysBounceHorizontal
水平方向否有弹簧效果
@property(nonatomic) BOOL alwaysBounceHorizontal;
这里不同的控件的默认值会有些差异
-
UIScrollView
和UICollectionView
默认情况下垂直alwaysBounceVertical
和水平alwaysBounceHorizontal
都是NO;只有当内容视图的尺寸超过了自己的bounds的尺寸的时候,相应方向上反弹属性才会自动设置为YES; -
UITableView
默认情况下垂直alwaysBounceVertical
是YES,水平alwaysBounceHorizontal
是NO;
获得滚动状态
开始触碰、拖拽、减速、停止等一些列属性
-
tracking
用户是否已经触摸了内容
@property(nonatomic, readonly, getter=isTracking) BOOL tracking;
如果用户仅仅是触摸、但并未拖动、也是YES。
比如我们在手指离开时为YES、在惯性滚动之后为NO。
2018-10-11 11:23:34.580538+0800 DocumentDemo[19889:1060480] scrollViewDidEndDragging - 1
2018-10-11 11:23:36.916507+0800 DocumentDemo[19889:1060480] scrollViewDidEndDecelerating - 0
-
dragging
获取用户是否开始拖动视图
@property(nonatomic, readonly, getter=isDragging) BOOL dragging;
- 惯性时 >> YES
- 回弹时 >> NO
- 代码设置滚动 >> NO
-
decelerating
获取视图是否开始减速(用户停止拖动但视图仍在滚动)
@property(nonatomic, readonly, getter=isDecelerating) BOOL decelerating;
-
decelerationRate
惯性速度的衰减速率
@property(nonatomic) UIScrollViewDecelerationRate decelerationRate;
UIScrollViewDecelerationRate
本身是一个浮点型、范围是0-1。系统为我们提供了两个参考值(当然你也可以自己设置):
UIScrollViewDecelerationRateNormal:0.998(默认值)
UIScrollViewDecelerationRateFast:0.99
滚动条 && 刷新控件
-
indicatorStyle
设置滑动条样式
@property(nonatomic) UIScrollViewIndicatorStyle indicatorStyle;
具体值为一个枚举
typedef NS_ENUM(NSInteger, UIScrollViewIndicatorStyle) {
UIScrollViewIndicatorStyleDefault, //默认
UIScrollViewIndicatorStyleBlack, //黑色风格
UIScrollViewIndicatorStyleWhite //白色风格
};
-
scrollIndicatorInsets
滑动条的位置(长短)。默认
UIEdgeInsetsZero
@property(nonatomic) UIEdgeInsets scrollIndicatorInsets;
需要注意的是UIEdgeInsets
结构体中有4个值。正好对应右侧、下方两个滑动条。
支付宝这种滑动条从屏幕中央开始的效果就是用了这个属性(我猜)
-
showsHorizontalScrollIndicator
-
showsVerticalScrollIndicator
水平/垂直的滑动条是否会被显示
@property(nonatomic) BOOL showsHorizontalScrollIndicator;
@property(nonatomic) BOOL showsVerticalScrollIndicator;
在滑动发生时、是否会显示出来。并不是一直显示
-
flashScrollIndicators
当你的滑动条短暂的显示一下
- (void)flashScrollIndicators;
你想让用户知道目前的位置时、可以用一下、而不是偷偷滚动一点点距离。
-
refreshControl
刷新控件(iOS10+)
@property(nonatomic, strong) UIRefreshControl *refreshControl;
你可以用UIRefreshControl
自己实现刷新的控件、这个我没看
滚动到指定区域
-
- scrollRectToVisible:animated:
将
UIScrollView
坐标系中的指定Rect
展示出来
- (void)scrollRectToVisible:(CGRect)rect
animated:(BOOL)animated;
- 宽高不能为0
- 如果已经可以展示则不再移动
触摸管理
能否响应某处子视图、事件传递相关
-
- touchesShouldBegin:withEvent:inContentView:
自定义手指触碰到显示内容时的默认行为
- (BOOL)touchesShouldBegin:(NSSet<UITouch *> *)touches
withEvent:(UIEvent *)event
inContentView:(UIView *)view;
当点击到了一个子View会触发这个方法。
如果返回YES(默认)、UIScrollView会把当前事件分发到子view去处理(调用对应touchesBegan
方法)、如果NO就不分发此事件。
需要注意的是:
只有系统识别到点击、而不是拖动动作才会下发。具体识别方法可以看本文中《UIScrollView >> 响应原理》处的解释。
-
- touchesShouldCancelInContentView:
是否取消与子视图相关的触摸、并恢复拖动
- (BOOL)touchesShouldCancelInContentView:(UIView *)view;
默认情况下有两种情况
- 如果视图不是UIControl对象
返回值为YES - 如果是UIControl对象
返回值为NO
也就是说默认情况下、我们长按一个UIButton只有、就不能继续滚动了。如果返回NO、那么一旦我们继续滚动、UIButton的事件则会被取消。
-
canCancelContentTouches
当长按后继续滚动时、是否给子视图传递取消动作的消息
@property(nonatomic) BOOL canCancelContentTouches;
默认设置为YES。
对于子View、长按后令UIScrollView
继续滚动、会收到touchesCancelled
消息。如果这个值为NO、则不会收到该消息。
-
delaysContentTouches
设置视图是否延迟处理触摸事件。默认YES
@property(nonatomic) BOOL delaysContentTouches;
不等待识别是否为拖动手势、直接将消息先下发给内容控件(touchesBegan
方法)。
缩放和移动
拖动、捏合的手势获取。缩放相关
-
panGestureRecognizer
返回ScrollView的拖动手势识别器
@property(nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer;
主要用于处理手势冲突、比如右滑返回等等需要获取具体手势的时候。
-
pinchGestureRecognizer
返回ScrollView的捏合手势识别器
@property(nonatomic, readonly) UIPinchGestureRecognizer *pinchGestureRecognize
-
- zoomToRect:animated:
缩放到特定区域、使其可见
- (void)zoomToRect:(CGRect)rect
animated:(BOOL)animated;
这个rect必须处在viewForZoomingInScrollView:
方法返回的View之中、也就是允许对该View进行缩放。
大于当前rect则缩小、否则放大。
该方法的主要用于双击缩放图片的效果:《iOS 图片缩放》
-
- setZoomScale:animated:
设置当前的缩放比例
- (void)setZoomScale:(CGFloat)scale
animated:(BOOL)animated;
新的scale
必须介于minimumZoomScale
和 maximumZoomScale
之间。
该方法的主要用于双击缩放图片的效果:《iOS 图片缩放》
-
maximumZoomScale
-
minimumZoomScale
允许缩放的最大/最小值
@property(nonatomic) CGFloat maximumZoomScale;
@property(nonatomic) CGFloat minimumZoomScale;
默认值都为1、如果想实现缩放效果必须设置。
-
zoomBouncing
当前的缩放比例是否超出设置的峰值
@property(nonatomic, readonly, getter=isZoomBouncing) BOOL zoomBouncing;
bounce
也就是意味着将会有一个回弹效果出现
-
zooming
是否正在进行缩放
@property(nonatomic, readonly, getter=isZooming) BOOL zooming;
-
bouncesZoom
当缩放超过阈值、是否有会回弹动画发生。默认YES
@property(nonatomic) BOOL bouncesZoom;
键盘管理
-
keyboardDismissMode
当开始滚动时、键盘消失的方式
@property(nonatomic) UIScrollViewKeyboardDismissMode keyboardDismissMode;
具体为一个枚举值、包含三个样式
typedef NS_ENUM(NSInteger, UIScrollViewKeyboardDismissMode) {
UIScrollViewKeyboardDismissModeNone,
UIScrollViewKeyboardDismissModeOnDrag, //手指滑动视图键盘就会消失
UIScrollViewKeyboardDismissModeInteractive, //表示键盘可以随着手指下滑而移出屏幕
};
这个键盘的控件、并不必须是scrollView的子视图。
索引
-
indexDisplayMode
滚动时索引的显示方式
@property(nonatomic) UIScrollViewIndexDisplayMode indexDisplayMode;
为UIScrollViewIndexDisplayMode
类型的枚举、有两种方式
typedef NS_ENUM(NSInteger, UIScrollViewIndexDisplayMode) {
UIScrollViewIndexDisplayModeAutomatic, // 索引将根据需要自动显示或隐藏
UIScrollViewIndexDisplayModeAlwaysHidden, // 索引将永远不会显示
} API_AVAILABLE(tvos(10.2));
TVOS专用
最后
本文主要是自己的学习与总结。如果文内存在纰漏、万望留言斧正。如果愿意补充以及不吝赐教小弟会更加感激。
参考资料
官方文档-UIScrollView
UIScrollView 实践经验
scrollViewDidEndScrollingAnimation和scrollViewDidEndDecelerating的区别
iOS 图片缩放