1. UIScrollView
是一个 UIView
。
每个 UIView
都有一个 bounds
和 frame
。当布局一个界面时,我们需要处理视图的 frame
。这允许我们放置并设置视图的大小。 视图的 frame
和 bounds
的大小总是一样的,但是他们的 origin
有可能不同(bounds
的原点是 (0,0)
点(就是 view
本身的坐标系统,默认永远都是(0,0)
点,除非调用了 setbounds
函数),而 frame
的原点却是任意的(相对于父视图中的坐标位置)) 弄懂这两个工作原理是理解 UIScrollView
的关键。
2.UIView
的 bounds
和 frame
工作原理
这里有个概念光栅化和组合 , 本人的简单理解:
- 光栅化 : 简单的说就是产生一组绘图指令并且生成一张图片(注意,这些图片并没有被绘制到屏幕上去;它们被自己的视图保持着留到下一个步骤用)
- 组合 : 光栅化之后,屏幕其实是空的,你什么都看不见,但是其实视图已经出现的了,这个时候这些图片便被一个接一个的绘制,并产生一个屏幕大小的图片,这便是组合, 这个时候,屏幕上就能看到你描绘的东西。再扯一点,视图层级对于组合如何进行扮演了很重要的角色:一个视图的图片被组合在它父视图图片的上面。然后,组合好的图片被组合到父视图的父视图图片上 面,就这样。。。最终视图层级最顶端是窗口(window),它组合好的图片便是我们看到的东西了。
小结 : 知道上面两种绘制图像的形式和过程之后 bounds
的为什么一般是 (0,0)
, frame
却是以父控件为坐标原点,相信都有点理解了。
在光栅化步骤中,视图并不关心即将发生的组合步骤,这时视图只关心一件事就是绘制它自己的 content
。这个绘制发生在每个视图的 drawRect:
方法中,在 drawRect:
方法被调用前,会为视图创建一个空白的图片来绘制 content
。这个图片的坐标系统是视图的 bounds
。几乎每个视图 bounds
的 origin
都是(0,0)
。因此,当在删格化图片左上角绘制一些东西的时候,你都会在 bounds
的origin
({x:0,y:0})
处绘制。在一个图片右下角的地方绘制东西的时候,你都会绘制在 {x:width, y:height}
处。如果你的绘制超出了视图的 bounds
,那么超出的部分就不属于删格化图片的部分了,并且会被丢弃。
在组合的步骤中,每个视图将自己光栅化图片组合到自己父视图的光栅化图片上面。视图的 frame
决定了自己在父视图中绘制的位置,frame
的 origin
表明了视图光栅化图片左上角相对父视图光栅化图片左上角的偏移量。所以,一个 origin
为 {x:20,y:15}
的 frame
所绘制的图片左边距其父视图 20 点,上边距父视图 15 点。因为视图的 frame
和 bounds
矩形的大小总是一样的,所以光栅化图片组合的时候是像素对齐的。这确保了 光栅化图片不会被拉伸或缩小。
小结 : bound
和 frame
大小一样,位置不同的原因。
组合的时候,视图图片的左上角会根据它 frame
的 origin
进行偏移,并绘制到父视图的图片上
// 组合点的X值 = 当前视图的frameX值 - 父视图的光栅化X值
CompositedPosition.x = View.frame.origin.x - Superview.bounds.origin.x(一般来说总是0);
// 组合点的Y值 = 当前视图的frameY值 - 父视图的光栅化Y值
CompositedPosition.y = View.frame.origin.y - Superview.bounds.origin.y(一般来说总是0);
3.UIScrollView
的 Content Offset
工作原理
正因为 iOS 这种组合原理, UIScrollView的工作原理就这样带出来了。
组合点的位置 (CompositedPosition.x , CompositedPosition.y) 就是子视图组合在父视图的位置。
根据上面的公式
改变 View.frame.origin.x 的值 和 Superview.bounds.origin.y的值, 都可以改变组合点值。
UIScrollView 的工作原理,就是改变scroll view.bounds的origin。
ScrollerView三大属性
-
contentSize
: 是scrollview
可以滚动的区域,比如frame = (0 ,0 ,320 ,480) contentSize = (320 ,960)
,代表你的scrollview
可以上下滚动,滚动区域为frame大小的两倍。 -
contentOffset
:scrollview
当前显示区域顶点相对于frame
顶点的偏移量,比如上个例子你拉到最下面,contentoffset就是(0 ,480)
,也就是y偏移了 480 -
contentInset
:scrollview
的contentview
的顶点相对于scrollview
的位置,例如你的contentInset = (0 ,100)
,那么你的contentview
就是从scrollview
的(0 ,100)
开始显示