前言
在上一篇文章中,我们学习了三方刷新库MJRefresh(巧用MJRefresh),同时我们也说了MJRefresh是基于UIScrollView的,在这篇文章中,我们将着重讲述一下UIScrollView的属性和方法的使用。
创建
UIScrollView
继承自UIView
和NSCoding协议
, 因此它的创建相当简单
let testScrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
// 设置背景颜色
testScrollView.backgroundColor = UIColor.black
// 设置代理
testScrollView.delegate = self
view.addSubview(testScrollView)
属性
-
contentOffset
偏移量,默认是CGPointZero,一般用在UIScrollView的代理方法里,用来做拖拽距离判断操作
例如:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
// 向下拉动偏移量大于等于20
if scrollView.contentOffset.y >= -20 {
print("now is \(scrollView.contentOffset.y)")
}
}
-
contentSize
滑动区域大小, 默认是CGSizeZero, width代表x方向滑动区域大小,height代表竖向滑动区域大小,一般为必选
例如:
testScrollView.contentSize = CGSize(width: 200 , height: 200 * 2)
-
contentInset
边缘插入内容以外的可滑动区域,默认是UIEdgeInsetsZero,top、bottom、left、right分别代表顶部和底部可滑动区域
例如:
testScrollView.contentInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
-
isDirectionalLockEnabled
默认为FALSE, 如果设置为TRUE,那么在推拖拽UIScrollView的时候,会锁住水平或竖直方向的滑动
例如:
testScrollView.isDirectionalLockEnabled = true
这个属性需要解释一下,这样说吧,当你的手准备滑动的时候,手按住UIScrollView不放,如果一开始滑动的方向是x方向,那么你就无法在y方向上移动(此时手还没有放开);如果一开始滑动的方向是y方向,那么你就无法在x方向上滑动(此时手还没有放开);如果一开始滑动的方向是倾斜方向(x、y均同时移动),那么你可以在任何方向随意滑动(此时手还没有放开)!
-
bounces
弹性效果,默认是TRUE, 如果设置成false,则当你滑动到边缘时将不具有弹性效果
例如:
testScrollView.bounces = false
-
alwaysBounceVertical
竖直方向总是可以弹性滑动,默认是NO, 当设置为TRUE(前提是属性bounces
必须为TRUE)的时候,即使contentSize
设置的width
和height
都比UIScrollView
的width
和height
小,在垂直方向上都可以有滑动效果,甚至即使我们不设置contentSize
都可以产生滑动效果; 反之,如果设置alwaysBounceVertical
为FALSE, 那么当contentSize
设置的width
和height
都比UIScrollView
的width
和height
小的时候,即使bounces
设置为TRUE,那么不可能产生弹性效果
例如:
在垂直方向有弹性效果
let testScrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testScrollView.backgroundColor = UIColor.black
testScrollView.delegate = self
testScrollView.bounces = true
testScrollView.alwaysBounceVertical = true
view.addSubview(testScrollView)
let testImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testImageView.backgroundColor = UIColor.green
testScrollView.addSubview(testImageView)
在垂直方向没有弹性效果
let testScrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testScrollView.backgroundColor = UIColor.black
testScrollView.delegate = self
// 因为contentSize为0,而且没有设置testScrollView.alwaysBounceVertical = true,所以即便testScrollView.bounces = true也没有弹性效果
testScrollView.bounces = true
view.addSubview(testScrollView)
let testImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
testImageView.backgroundColor = UIColor.green
testScrollView.addSubview(testImageView)
-
alwaysBounceHorizontal
用法完全同alwaysBounceVertical
-
isPagingEnabled
是否可分页,默认是FALSE, 如果设置成TRUE, 则可分页
例如:
let testScrollView = UIScrollView(frame: CGRect(x: 50, y: 64, width: UIScreen.main.bounds.width - 100, height: 200))
testScrollView.backgroundColor = UIColor.black
testScrollView.contentSize = CGSize(width: UIScreen.main.bounds.width - 100 , height: 200 * 2)
testScrollView.delegate = self
testScrollView.isPagingEnabled = true
view.addSubview(testScrollView)
let testImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width - 100, height: 200))
testImageView.backgroundColor = UIColor.green
testScrollView.addSubview(testImageView)
let testView = UIImageView(frame: CGRect(x: 0, y: 200, width: UIScreen.main.bounds.width - 100, height: 200))
testView.backgroundColor = UIColor.red
testScrollView.addSubview(testView)
演示效果
-
isScrollEnabled
是否可滑动,默认是TRUE, 如果默认为FLASE, 则无法滑动 -
showsHorizontalScrollIndicator
是否显示水平方向滑动条,默认是TRUE, 如果设置为FALSE,当滑动的时候则不会显示水平滑动条 -
showsVerticalScrollIndicator
是否显示垂直方向上滑动条,默认是TRUE, 如果设置为FALSE,当滑动的时候则不会显示垂直方向上的滑动条 -
scrollIndicatorInsets
滑动条的边缘插入,即是距离上、左、下、右的距离
例如:
// 当向下滑动时,滑动条距离顶部的距离总是20
testScrollView.scrollIndicatorInsets = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
-
indicatorStyle
设置滑动条颜色, 默认是灰白色
indicatorStyle
是个枚举类型:
public enum UIScrollViewIndicatorStyle : Int {
case `default` // 灰白色,搭配任意背景色
case black // 黑色,搭配白色背景最佳
case white // 白色,搭配黑色背景最佳
}
-
decelerationRate
减速率,CGFloat类型,当你滑动松开手指后的减速速率, 但是尽管decelerationRate
是一个CGFloat类型,但是目前系统只支持以下两种速率设置选择
1 UIScrollViewDecelerationRateNormal 值是 0.998
2 UIScrollViewDecelerationRateFast 值是 0.99
例如设置
// 快速减速
testScrollView.decelerationRate = UIScrollViewDecelerationRateFast
-
indexDisplayMode
索引展示模式,是一个枚举值,有两种选择
public enum UIScrollViewIndexDisplayMode : Int {
case automatic // 根据需要自动显示或隐藏
case alwaysHidden // 总是隐藏
}
事实上,这个属性我并没有试出任何的作用,我也不知道作用是什么?如果你知道,请告诉我!此外,我现在使用的环境是Xcode 8.3, 而这个属性似乎是在8.3以后才能用?看下面官方截图
结果:控制台没有输出任何日志,也就是没有响应button事件,并且可以看到button向上滑动,实质上是触发了UIScrollView的滑动事件!
02 不子类化UIScrollView,默认设置(true):操作方法,长按button向上滑动
结果:控制台输出“触发滑动视图上面的UIButton事件”, 响应了button事件,可以看到button没有向上滑动,也就是没有触发UIScrollView的滑动事件!
03 不子类化UIScrollView,设置false: 操作方法,点击button并快速向上滑动或者长按上滑
结果:当点击button并且同时快速上滑时或者长按上滑时,响应了button的事件,控制台输出“触发滑动视图上面的UIButton事件”, 没有看到button向上滑动,说明没有触发UIScrollView的滑动事件!
不子类化UIScrollView属性小结(个人认为): 一般情况下,我们使用该属性的默认值即可,因为如果不延迟内容触摸,优先响应UIScrollView上面的子控件,这样很容易造成误操作,比如上面的例子,一般用户的行为操作是希望向上滑动,但是只是不小心一开始触摸到的是一个button,却执行了button事件跳转到另一个页面或者toast提示或者其他操作,这显然不是用户所期望的,因为用户希望能够向上滑动,查看更多的状态等等!!!
不子类化UIScrollView注意点: 在上面的01和02操作中,为什么在都是设置为true的情况下,点击button快速上滑是响应UIScrollView事件,但是长按button上滑却是响应button事件呢?这就牵扯到UIScrollView的内部原理了,在UIScrollView中,在有一个计时器用来判断用户当前操作在UIScrollView上面的子视图的响应事件时长(这里以button为例,设UIScrollView内部默认时长为x),当用户操作时长小于x时(比如点击按钮立即上滑),UIScrollView内部机制会将触发事件传递给UIScrollView处理,则响应UIScrollView事件; 当用户操作时长大于x(比如长按按钮上滑),UIScrollView内部机制就会将触发事件返回交由它的子视图button来处理,也即是认为用户是想要操作button而并非UIScrollView!
04 子类化UIScrollView,默认设置为true:操作方法,点击按钮立即上滑
代码:
class ViewController: UIViewController, UIScrollViewDelegate {
private var testScrollView: SubScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// 使用的是子类化UIScrollView
testScrollView = SubScrollView(frame: CGRect(x: 50, y: 64, width: UIScreen.main.bounds.width - 100, height: 200))
testScrollView.contentSize = CGSize(width: UIScreen.main.bounds.width - 100 , height: 200 * 4)
testScrollView.delegate = self
view.addSubview(testScrollView)
let label1 = UILabel(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width - 100, height: 200))
label1.backgroundColor = UIColor.green
label1.text = "第一页"
label1.font = UIFont.boldSystemFont(ofSize: 17)
testScrollView.addSubview(label1)
let label2 = UILabel(frame: CGRect(x: 0, y: 200, width: UIScreen.main.bounds.width - 100, height: 200))
label2.text = "第二页"
label2.numberOfLines = 0
label2.backgroundColor = UIColor.red
label2.font = UIFont.boldSystemFont(ofSize: 17)
testScrollView.addSubview(label2)
let label3 = UILabel(frame: CGRect(x: 0, y: 400, width: UIScreen.main.bounds.width - 100, height: 200))
label3.text = "第三页(最后一页)"
label3.backgroundColor = UIColor.blue
label3.font = UIFont.boldSystemFont(ofSize: 17)
testScrollView.addSubview(label3)
let btn = UIButton(type: .custom)
btn.frame = CGRect(x: 100, y: 200 * 3 + 100, width: 100, height: 50)
btn.backgroundColor = UIColor.red
btn.setTitle("test button", for: .normal)
btn.setTitleColor(UIColor.black, for: .normal)
btn.addTarget(self, action: #selector(buttonAc), for: .touchUpInside)
testScrollView.addSubview(btn)
}
func buttonAc() {
print("触发滑动视图上面的UIButton事件")
}
}
子类化UIScrollView代码:
class SubScrollView: UIScrollView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.blue
// 默认为true
// delaysContentTouches = false
}
override func touchesShouldCancel(in view: UIView) -> Bool {
super.touchesShouldCancel(in: view)
print("Test touches should cancel! current responding view is \(view)")
return true
}
override func touchesShouldBegin(_ touches: Set<UITouch>, with event: UIEvent?, in view: UIView) -> Bool {
super.touchesShouldBegin(touches, with: event, in: view)
print("Test touches should begin! current responding view is \(view)")
return true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
好,看一下演示效果:
结果:点击按钮立即上滑,在默认设置(true)下,控制台没有输出button事件的打印值,按钮上移,所以是优先触发UIScrollView响应事件!另外,没有触发
touchesShouldBegin方法
,原因是delaysContentTouches
没有设置成false
,所以不会触发!
05 子类化UIScrollView,默认设置为true:操作方法,长按button上滑
演示效果:
结果:长按button上滑,没有触发button方法中的事件,button上移,控制台打印
touchesShouldBegin
和touchesShouldCancel
日志,说明响应的还是UIScrollView事件!这里又是怎么回事呢?还是之前提到的这是由于UIScrollView的内部机制,在04中,因为触发button的时间极其短,小于延迟内容触摸的时间x,所以UIScroll直接接收了事件的响应,没有将事件返回给它的子视图button!!而长按button的时候,触发button的时间已经达到内容触摸的时间x(大于x),所以UIScrollView内部判断了用户应该是要响应button事件,所以将事件返回给button,那么问题来了,既然事件已经返回给button了,但是为什么没有打印button方法中的 “触发滑动视图上面的UIButton事件” 呢?更重要的是,为何button上移了,说明响应了UIScrollView事件呢?这又和所复写的touchesShouldCancel
相关了,因为该方法返回的bool值是true,所以相当于你(UIScrollView)把事件返回给我(button)后,后面又touchesShouldCancel
返回true来取消我的响应,真实让我(button)空欢喜一场了!
好,既然你UIScrollView想要我button响应事件,那你后面就不要反悔来取消我的事件啊,那么你就该在touchesShouldCancel
中返回false来跟我说:我不取消你的任务了,你做吧!!!于是我们在touchesShouldCancel
中将返回值设置成false看看会怎么样,其他不变,然后:
override func touchesShouldCancel(in view: UIView) -> Bool {
super.touchesShouldCancel(in: view)
print("Test touches should cancel! current responding view is \(view)")
return false
}
演示效果:
结果:button没有上移,button方法中的事件被响应,执行了
touchesShouldBegin
(响应UIScrollView上面的子视图button)和touchesShouldCancel
(返回false告诉button,把事件交给button处理)
06 子类化UIScrollView,默认设置为false:操作方法,点击button立即上滑、点击button长按上滑:
import UIKit
class SubScrollView: UIScrollView {
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.blue
// 设置为false
delaysContentTouches = false
}
override func touchesShouldCancel(in view: UIView) -> Bool {
super.touchesShouldCancel(in: view)
print("Test touches should cancel! current responding view is \(view)")
// 这里返回true,UIScrollView取消子视图的响应
return true
}
override func touchesShouldBegin(_ touches: Set<UITouch>, with event: UIEvent?, in view: UIView) -> Bool {
super.touchesShouldBegin(touches, with: event, in: view)
print("Test touches should begin! current responding view is \(view)")
return true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
演示效果:
同样地,从上面我们可以看到,当
delaysContentTouches = false
时,也就是不延迟内容触摸(优先响应button),但实际情况是,我们看到button上移,button方法中的事件没有被响应,touchesShouldBegin
和touchesShouldCancel
方法被调用,也就是最终响应了UIScrollView事件!!这又是什么样的奇怪现象呢?事实上,尽管你设置了``delaysContentTouches = false, 但是最终决定权还是在UIScrollView的手里,它有一个致命武器就是
touchesShouldCancel`返回true来取消你的响应,UIScrollView在告诉你:小子(button),本跟我耍花样,你完全在我的掌握之中,我想让你干你就干,不想让你干你就给我走人!
于是,我们除了设置``delaysContentTouches = false`外,还要:
override func touchesShouldCancel(in view: UIView) -> Bool {
super.touchesShouldCancel(in: view)
print("Test touches should cancel! current responding view is \(view)")
return false
}
现在来看一下效果:
结果:我们看到,当我们直接点击button,和长按button立即上滑时,button不上移,button事件被响应,说明最终并未响应UIScrollView中的时间!但是,我们发现一个奇怪的现象,就是当我点击button立即上滑(演示中的第5、6、7下操作)时,button没有上移,但是button中的方法也没有被调用,说明什么?说明似乎既没有响应button事件,也没有响应UIScrollView中的事件?这个现象就十分奇怪了,事实上,button事件依然被响应,只不过我们别忘记了一点,点击button的模式,现在是
.touchUpInside
,如果改成touchUpOutside
就会响应了,最好的验证方式是将这里得button换成一个UIView,然后给UIView添加一个向上轻扫的手势UISwipeGestureRecognizer
,然后就可以验证接收的事件的确是当前UIScrollView的子视图而并非UIScrollView了。
-
canCancelContentTouches
可以取消内容触摸, 默认是true,可以优先响应UIScrollView(直接点击button除外除外);设置成false,则不取消内容触摸,也即是优先响应响应子视图,但是在延迟内容触摸的时间x内除外!
设置成true(默认):
结果:除了立即点击一下button会调用button的方法外,快速点击button上滑,button上移,没有走
touchesShouldBegin
和touchesShouldCancel
方法,触发的时间小于延迟触摸内容时间x,所以直接响应UIScrollView滑动;当按下button停留一下上滑时,button依旧上移,但是走了touchesShouldBegin
和touchesShouldCancel
方法,触发的时间大于延迟触摸内容时间x,本应该调用button中的方法,但是由于UIScrollView让touchesShouldCancel
返回true取消了button的调用,所以还是走UIScrollView的响应!
设置成false:
结果:我们发现,在将
canCancelContentTouches
设置成false后,则永远不会调用touchesShouldCancel
方法, 当快速点击button上滑或者长按住button直接立即上滑时,button上移,响应了UIScrollView事件,没有走touchesShouldBegin
和touchesShouldCancel
方法, 触摸时间小于延迟内容触摸时间x,所以响应UIScrollView; 当点击button停顿一下后(不松手)继续上滑时,button没有上移,触摸时间大于延迟内容触摸时间x,button事件被响应,走了touchesShouldBegin
方法,响应了button点击事件;
-
minimumZoomScale
滑动视图的最小缩放倍数,默认是1.0 -
maximumZoomScale
滑动视图的最大缩放倍数,默认是1.0(要使得缩放有效果,maximumZoomScale
必须要大于minimumZoomScale
)
例如:
class ViewController: UIViewController, UIScrollViewDelegate {
private var testScrollView: UIScrollView!
private var testImgView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
testScrollView = UIScrollView(frame: CGRect(x: 50, y: 264, width: UIScreen.main.bounds.width - 100, height: 200))
testScrollView.contentSize = CGSize(width: UIScreen.main.bounds.width - 100 , height: 200)
testScrollView.delegate = self
testScrollView.backgroundColor = UIColor.orange
testScrollView.minimumZoomScale = 0.5
testScrollView.maximumZoomScale = 2
view.addSubview(testScrollView)
testImgView = UIImageView(frame: testScrollView.bounds)
testImgView.image = #imageLiteral(resourceName: "testimage2.jpg")
testScrollView.addSubview(testImgView)
}
func viewForZooming(in scrollView: UIScrollView) -> UIView? {
return testImgView
}
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
let offSetX = scrollView.bounds.width > scrollView.contentSize.width ? (scrollView.bounds.width - scrollView.contentSize.width) * 0.5 : 0.0
let offSetY = scrollView.bounds.height > scrollView.contentSize.height ? (scrollView.bounds.height - scrollView.contentSize.height) * 0.5 : 0.0
testImgView.center = CGPoint(x: scrollView.contentSize.width * 0.5 + offSetX, y: scrollView.contentSize.height * 0.5 + offSetY)
}
}
演示效果:
注意:需要实现缩放效果,代理必须要实现
func viewForZooming(in scrollView: UIScrollView) -> UIView?
方法,否则无法实现缩放功能,必要时要达到缩放后的一些效果操作还要实现代理的func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat)
方法!
-
zoomScale
当前的缩放比例, 默认是1.0
例如
func tap() {
// 双击当前图片,使其缩放成原来的0.8倍
testScrollView.zoomScale = 0.8
// 使图片居中
let offSetX = testScrollView.bounds.width > testScrollView.contentSize.width ? (testScrollView.bounds.width - testScrollView.contentSize.width) * 0.5 : 0.0
let offSetY = testScrollView.bounds.height > testScrollView.contentSize.height ? (testScrollView.bounds.height - testScrollView.contentSize.height) * 0.5 : 0.0
testImgView.center = CGPoint(x: testScrollView.contentSize.width * 0.5 + offSetX, y: testScrollView.contentSize.height * 0.5 + offSetY)
}
-
bouncesZoom
弹性缩放,默认是true, 设置成false的话,当缩放到最大或最小值的时候不会有弹性效果 -
isZooming
正在缩放,get属性,正在进行缩放的时候是true, 松开就是false -
isZoomBouncing
get属性,当没有设置bouncesZoom
的时候,如果正在缩放过程中则为false,如果缩放到最小值或者最大值时松开手指则为true; 当设置bouncesZoom = false
的时候,如果正在缩放过程中zoomScale > 1
时则为false,并且缩放到最大值时松开手指也是false。 -
scrollsToTop
滑动到顶部,默认是true,当点击状态栏的时候,如果当前UIScrollView不是处在顶部位置,那么可以直接回到顶部;如果已经在顶部,则没有作用;另外必须注意如果要这个属性起作用,它的delegate方法func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool
不能返回false
,否则没用。 -
panGestureRecognizer
平移手势,get属性,可以通过设置平移手势的属性来改变平移方式,比如设置触摸手指的最少个数minimumNumberOfTouches
-
pinchGestureRecognizer
捏合手势,也就是缩放手势,get属性,设置同平移手势一样,当缩放禁用时返回nil。 -
keyboardDismissMode
键盘消失模式, 默认是none,是个枚举值
public enum UIScrollViewKeyboardDismissMode : Int {
case none // 无
case onDrag // 拖拽,只要滑动UIScrollView,键盘消失
case interactive // 交互式,拖住UIScrollView一直下滑,当接触到键盘时,键盘就跟着同步下滑
}
演示一下interactive:
testScrollView.keyboardDismissMode = .interactive
-
refreshControl
自带的刷新控件(iOS 10.0以后才有的,很少用到)
testScrollView.refreshControl = UIRefreshControl()
演示效果
当然,这个属性还可以在菊花下方添加一个title,但是具有一定的透明度,不是那么好用,尝试过很多方法难以改变,字体和颜色是可以设置的,希望后期Apple可以优化,以至于好用!
方法
-
setContentOffset(_ contentOffset: CGPoint, animated: Bool)
设置或者指定偏移量(动画)
如果我们需要在某一处触发某个事件就滑动到指定的位置,就可以使用这个方法,十分有用,看下面的例子
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
// testScrollView的frame的height为200, 现在可以使用这个方法滑到最后一页
testScrollView.setContentOffset(CGPoint(x: 0, y: 200 * 2), animated: true)
}
演示效
-
scrollRectToVisible(_ rect: CGRect, animated: Bool)
滑动到指定的可见区域(带动画),意思就是滑动到CGRect所组成的矩形区域,使其可见!
解释一下,这个方法中的
CGRect
中的width
和height
必须要设置才能起作用,而且必须大于0!!!否则设置无效! 另外,如果当前区域已经可见,那这个方法什么都不做,什么意思呢?比如现在我所在区域坐标为 (x: 0, y: 0, width: 200, height:200),testScrollView.scrollRectToVisible(CGRect(x: 0, y: 20, width: 100, height: 150), animated: true)
, 那么这个方法就不会起作用,因为20(y坐标) + 150(height) = 170 < 200
,当前区域已经包含了scrollRectToVisible
所要滑动可见的区域 !
-
flashScrollIndicators()
滑动条闪烁
......................................
// 当页面加载成功出现时,滑动条会自动显示出来,停留一下又自动隐藏
// 不设置的话,页面出现时也不会显示滑动条,只有在滑动过程中会显示滑动条
testScrollView.flashScrollIndicators()
view.addSubview(testScrollView)
-
touchesShouldBegin(_ touches: Set<UITouch>, with event: UIEvent?, in view: UIView) -> Bool
触摸事件开始, 这个前面讲delaysContentTouches
和canCancelContentTouches
属性的时候也讲得差不多了,方法的意思是开始响应UIScrollView的子视图(比如button)的事件。此外,这个方法必须要子类化UIScrollView即复写父类才可以起到作用,用来控制UIScrollView的子视图的触摸事件的传递, 在触摸事件传递到UIScrollView的子视图之前就会被调用,如果返回false则触摸事件一定不会传递给子视图,但是直接点击除外,因为直接点击子视图,子视图立马响应自己的方法! -
touchesShouldCancel(in view: UIView) -> Bool
触摸事件取消,返回true直接取消UIScrollView的子视图的响应,直接由UIScrollView响应;返回false则会响应UIScrollView的子视图事件; -
func setZoomScale(_ scale: CGFloat, animated: Bool)
当前的缩放比例(可带动画)
例如
func doubleTap() {
testScrollView.setZoomScale(0.5, animated: true)
}
-
func zoom(to rect: CGRect, animated: Bool)
缩放指定的区域
例如:
testScrollView = UIScrollView(frame: CGRect(x: 50, y: 264, width: 300, height: 100))
testScrollView.contentSize = CGSize(width:300 , height: 100)
testScrollView.backgroundColor = UIColor.orange
testScrollView.minimumZoomScale = 0.1
testScrollView.maximumZoomScale = 4
testScrollView.delegate = self
view.addSubview(testScrollView)
func doubleTap() {
testScrollView.zoom(to: CGRect(x: 0, y: 0, width: 100, height: 50), animated: false)
print("current zoomScale is \(testScrollView.zoomScale)")
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
print("contentoffset x is \(scrollView.contentOffset.x)")
print("contentoffset y is \(scrollView.contentOffset.y)")
print("contentSize: \(scrollView.contentSize)")
}
这个方法使用要注意几点:
1、func zoom(to rect: CGRect, animated: Bool)
方法中的CGRect
中的x、y不能设置成负数,设置负数的话,默认x、y为0;
2、 zoomScale = min(UIScrollView.width / zoom.width, UIScrollView.height / zoom.height)
3、
contentSize.height = zoomScale * UIScrollView.height
contentSize.width = zoomScale * UIScrollView.width
4、 下面只是相对值,大多数情况下精确,少数情况下有少许误差!
zoom.y * zoomScale >= (UIScrollView.height / zoomScale - zoom.height)
zoom.x * zoomScale >= (UIScrollView.width / zoomScale - zoom.width)
contentOffSet.y = zoom.y * zoomScale - (UIScrollView.height / zoomScale - zoom.height)
contentOffSet.x = zoom.x * zoomScale - (UIScrollView.width / zoomScale - zoom.width)
到这里看得好累有没有??其实我也写得很累,继续搬砖
看一下UIScrollView的UIScrollViewDelegate方法
-
func scrollViewDidScroll(_ scrollView: UIScrollView)
已经滑动,常用来处理一些偏移量,判断拖拽状态等 -
func scrollViewDidZoom(_ scrollView: UIScrollView)
已经缩放,处理一些缩放操作 -
func scrollViewWillBeginDragging(_ scrollView: UIScrollView)
即将开始拖拽(或许要拖拽移动一小段距离才会调用),只要一开始拖拽就会调用 -
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>)
即将松开手指结束拖拽 -
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool)
已经松开手指,结束拖拽状态 -
func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView)
即将开始减速 -
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView)
已经结束减速, 当UIScrollView停止时就调用 -
func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView)
已经结束滑动动画,这个方法起作用的前提是设置了上面提到的两种方法中的任意一种,否则不会起作用!
1 func setContentOffset(_ contentOffset: CGPoint, animated: Bool)
2 func scrollRectToVisible(_ rect: CGRect, animated: Bool)
-
func viewForZooming(in scrollView: UIScrollView) -> UIView?
返回一个需要缩放的视图,需要做缩放的时候必须调用此方法,之前已经讲过,不再赘述! -
func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?)
即将开始缩放,在滑动视图开始缩放它的内容视图前调用 -
func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat)
已经结束缩放状态,结束缩放手势时调用,在最小和最大值之前进行缩放 -
func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool
滑到顶部,这个方法和属性scrollsToTop
用法一致,本质一致,返回true则是可以滑动顶部,false反之! -
func scrollViewDidScrollToTop(_ scrollView: UIScrollView)
已经滑到顶部,当滑动到顶部的动画完成的时候调用
小结
到此,UIScrollView基本差不多,我们详细讲解了它的一些基本属性和用法,甚至包括一些具体的区别和细节,但是还有一些更深入的底层原理到底怎么样实现的呢?包括它的滑动手势控制,定时器设置,偏移量等还有很多工作需要做,怎么做,还有待研究,有时间会继续深入更新...
</br>
</br>
欢迎加入 iOS(swift)开发互助群:QQ群号:558179558, 相互讨论和学习!
网友评论