深入浅出了解frame和bounds

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出了解frame和bounds相关的知识,希望对你有一定的参考价值。

参考技术A

frame的官方解释如下:

它定义了一个view相对于父视图坐标系的位置和大小,它会影响center属性和bounds属性的size。
先看一下它究竟是什么?
它是一个CGRect类型,如下

其中的origin就是该view的位置,它是一个CGPoint类型,也是一个结构体,包含了我们熟知的常用二维坐标系的x、y。根据x、y可以在坐标系里面唯一确定一个点。如下图:

这个坐标系原点是左上角,而非左下角。
关于frame,这里要注意的一点就是:frame是相对于父视图的坐标系来定位的。如果你这样设置frame:(0,0,100,200),也就是在父视图左上角添加了一个宽100,高200的子视图(前提是没有改变父视图的bounds,接下来会有介绍bounds)。

它也是描述的是视图的位置和大小,只不过是在自己的坐标系上。也就是说它描述的是当前视图相对于自身坐标系的位置和大小。

举个例子:

输出的结果如下:

由此可见,如果我们没有去更改bounds的值,它默认的位置坐标点是(0,0)

center是view的中点。该属性是想归于父类的坐标系确定的。从bounds小节里面的例子可以看到center的值,其计算方法为:
center.x = frame.origin.x + frame.size.width/2
center.y = frame.origin.y + frame.size.height/2

它用于指定视图的变换。使用这个属性可以放大或者旋转视图,它的frame会因此改变,是以中心点为变换的。看例子:

看输出的结果:

如图:

可以看出,当我们对图像通过旋转,旋转后的图片的frame已经变成了(2.5773352536321568, 59.226689885086444), (314.84532949273569, 449.54662022982711),此时的起始位置为图上旋转后标的(2.58,59.2),大小也变成了双箭头黑线标注的大小。

因此得出结论:进行了transform变换,其frame改变了,但是其bounds和center并没有修改。此时bounds的size和frame的size已经没有关系了。当没有进行任何transform时,frame的size总是和bounds相等。

以上便是对frame、bounds、center和transform做了一个简单的介绍。

接下来看一个例子(例子A):

这里在parentView上添加了一个childView,然后对parentView的bounds进行修改和不修改进行了测试,结果如下:

你会发现当修改了parentView的bounds之后,发现childView缺向右向下做了偏移。这里设置parentView的bounds的origin为(-40,-40)为何会发生这种情况呢?接下来先看一下下面这张图

如果此时我们没有改变图中O的坐标,那么此时A的坐标是(20,20),如果我们更改了O的坐标为(-20,-20),那么原来A点的坐标就成了A\'(0,0),但是A坐标是不变的,所以它会到黑色A处。所以你改变了原点坐标为负之后,A点会移动到黑色A。相反如果你设置了坐标原点为(20,20),那么A点就会和坐标原点重合。

这就是为什么childView会向右向下移动的原因。

接下来再做如下操作(例子B):

输出结果如下:

运行效果是childView向上移动,然后停止。结果前后对比图如下:

直观来看,按说childView的frame改变了,但是从console输出的结果来看,childView的frame/bounds/center都没有改变,但是直观来看其位置却改变了。再看一下parentView,只有bounds改变了,frame和center却没变,从直观来看parentView没有任何更改。所以很有可能是parentView的bounds修改引起了childView的位置更改。这是为什么呢?这里先不说明为什么,再看一下最常用的UIScrollView:

当滚动视图的时候,console输出结果如下:

根据输出结果可以看到,parentView的center、frame、bounds在滚动过程中都没有作出更改,但是我们看到的它的位置的确改变了。而对于scrollView来说,其frame和center也没有更改,但是bounds更改了。

这种现象和上面提到的(例子B)的现象一样,都是对bounds进行了修改。然后子视图从新进行了布局。说道子视图重新布局,让我想到了一个方法:

从字面意思看就是布局某个视图的子视图,那么会不会和这个方法有关呢?因此我在自定义的ZGUIScrollView里面实现了该方法:

再次滚动界面,发现每次滚动都会调用scrollview的layoutSubViews方法。苹果官方文档介绍:

它的作用就是布局一个视图上的子视图。确定子视图的大小和位置。如果你想强制布局更新,你不能直接去调用这个方法,而是在下次更新图形之前调用setNeedsLayout方法,如果你要立即更新视图布局,调用layoutIfNeeded方法。

由此可知,UIScrollView的实现就是通过bounds来实现的。contentOffset是bounds的origin。然后当bounds修改之后,会在layoutSubviews方法里面对子视图进行布局。对子类进行更新。

另外,我们还可以用bounds实现如下效果:

图上右侧便是使用了bounds实现的效果。实现方式就是在自定义cell中重写drawReact:

其实UITableView(它是UIScrollView)的实现也是类似,更改了bounds,来实现滚动加载cell。

总结

对bounds和frame的理解就是这些,其实系统用bounds的地方还是很多的。例如UIScrollView的实现就用到了。有疑问的话可以留言交流。

以上是关于深入浅出了解frame和bounds的主要内容,如果未能解决你的问题,请参考以下文章

技术分享-bounds的深入认识

iOS frame和Bounds 以及frame和bounds区别

关于 bounds 和 frame

iOS frame和Bounds 以及frame和bounds区别

进一步理解 frame 和 bounds

frame和bounds