当前位置: 首页 > 科技观察

全面了解iOS开发中的ScrollView

时间:2023-03-21 18:55:47 科技观察

,你可能很难相信。UIScrollView和标准UIView之间没有太大区别。滚动视图确实有更多的方法,但这些方法只是UIView的一些属性的表面。因此,在了解UIScrollView的工作原理之前,需要了解UIView,尤其是视图渲染过程的两个步骤。光栅化和组合渲染过程的第一部分称为光栅化,简单来说就是生成一组绘图指令并生成图像。例如,绘制一个圆角矩形,带有图片和居中标题的UIButtons。这些图像不会绘制到屏幕上;相反,它们由自己的视图持有,以供下一步使用。一旦每个视图都生成了自己的光栅化图像,这些图像就会一个接一个地绘制以生成屏幕大小的图像,这就是上面描述的组合。视图层次结构在组合的工作方式中起着重要作用:视图的图像是在其父图像之上组合的。然后,合成图像被合成在父视图的父视图图像之上,仅此而已。..最终视图层级的最顶端是窗口,组合后的图片就是我们看到的。从概念上讲,依次在每个视图上放置独立的分层图像并最终生成单个单调的图像将变得更容易理解,尤其是如果您以前使用过Photoshop等工具。我们有另一篇文章详细解释了如何将像素绘制到屏幕上。现在,回想一下每个视图都有一个边界和一个框架。在布局界面时,我们需要处理视图的框架。这允许我们定位和设置视图的大小。视图的框架和边界总是大小相同,但它们的来源可能不同。理解这两个工作原理是理解UIScrollView的关键。在光栅化步骤中,视图不关心即将到来的合成步骤。也就是说,它不关心它的框架(这是用来放置视图的图像)或者它在视图层次结构中的位置(它决定了组合的顺序)。此时视图唯一关心的是绘制它自己的内容。此绘图发生在每个视图的drawRect:方法中。在调用drawRect:方法之前,为视图创建一个空白图像以在其上绘制内容。该图像的坐标系是视图的边界。几乎每个视图边界的原点都是{0,0}。因此,当您在网格化图像的左上角绘制内容时,您是在边界的原点({x:0,y:0})绘制的。在图像的右下角绘制内容时,您将绘制在{x:width,y:height}处。如果您的绘图超出了视图的边界,则超出部分不是网格化图像的一部分,将被丢弃。在合成步骤中,每个视图将自己的光栅化图像合成到其父视图的光栅化图像上。视图的框架决定了它在父视图中的绘制位置,框架原点表示视图光栅化图像左上角相对于父视图光栅化图像左上角的偏移量。因此,由原点为{x:20,y:15}的框架绘制的图片与其父视图的左边距为20点,与其父视图的上边距为15点。因为视图的框架和边界矩形总是相同大小,所以光栅化图像在组合时是像素对齐的。这可确保光栅化图像不会被拉伸或收缩。请记住,我们只是在谈论视图与其父视图之间的组合。两个视图合并后,合并后的图像将与父视图的父视图合并。..这是滚雪球效应。考虑组合图片背后的公式。视图图像的左上角将根据其框架的原点进行偏移,并绘制在父视图的图像上:CompositedPosition.x=View.frame.origin.x-Superview.bounds.origin.x;CompositedPosition.y=视图。frame.origin.y-Superview.bounds.origin.y;我们可以通过几个不同的框架来查看:这样做是有意义的。当我们改变按钮的frame.origin后,它会改变它相对于紫色父视图的位置。请注意,如果我们移动按钮直到它的一部分已经超出紫色超级视图的边界,那么当光栅化图像被同一绘图裁剪时,该部分将被裁剪。然而,从技术上讲,由于iOS处理组合的方式,您可以在其父视图的边界之外渲染子视图,但在光栅化期间绘制不能超出视图的边界。ScrollView的ContentOffset现在,我们所说的与UIScrollView有什么关系呢?这就是它的全部!考虑一种我们可以实现的滚动:我们有一个视图,它的框架随着我们的拖动而改变。这达到了同样的效果,对吧?如果我向右拖动手指,那么在拖动的同时我会增加视图的origin.x,瞧,这是滚动视图。当然,滚动视图中还有很多具有代表性的视图。要实现此平移功能,您需要在用户移动手指时更改每个视图的框架。当我们建议将视图的光栅化图像与其父视图所在的位置组合时,请记住以下公式:CompositedPosition.x=View.frame.origin.x-Superview.bounds.origin.x;CompositedPosition.y=View.frame。origin.y-Superview.bounds.origin.y;我们减少Superview.bounds.origin的值(因为它们总是0)。但是,如果它们不为0怎么办?我们使用与上图相同的帧,但我们将紫色视图边界的原点更改为{-30,-30}。得到下图:现在,通过巧妙地改变这个紫色视图的边界,它的每个单独的子视图都被移动了。事实上,这正是滚动视图的工作原理。当您设置其contentOffset属性时:它会更改滚动view.bounds的原点。事实上,contentOffset根本不存在。代码是这样的:-(void)setContentOffset:(CGPoint)offset{CGRectbounds=[selfbounds];bounds.origin=offset;[selfsetBounds:bounds];}注:前面的图例,刚好可以改变原点bounds,按钮将在紫色视图和按钮的组合图像的边界之外。这也是当你把scrollview移动够了,一个view就会消失!世界之窗:ContentSize现在,最难的部分已经结束,让我们看看UIScrollView的另一个属性:contentSize。滚动视图的内容大小不会改变其边界的任何内容,因此它不会影响滚动视图如何组成其子视图。相反,内容大小定义了可滚动区域。滚动视图的默认内容大小为{w:0,h:0}。由于没有可滚动区域,用户无法滚动,但滚动视图仍会显示其范围内的所有子视图。当内容大小设置为大于边界时,用户可以滚动视图。您可以将滚动视图的边界视为可滚动区域上的窗口:当内容偏移量为{x:0,y:0}时,可见窗口的左上角位于可滚动区域的左上角。这也是最小的内容偏移量;用户不能再移动到可滚动区域的左侧或顶部。那里什么都没有,出去!内容偏移量的最大值是内容大小与滚动视图大小之间的差值。这也是有道理的:从左上角滚动到右下角,当用户停止时,滚动区域的右下边缘与滚动视图边界的右下边缘齐平。您可以这样记下内容偏移的最大值:contentOffset.x=contentSize.width-bounds.size.width;contentOffset.y=contentSize.height-bounds.size.height;用ContentInsets稍微调整窗口,contentInset属性可以改变content偏移的最大值和最小值,使其可以滚动出可滚动区域。它是UIEdgeInsets类型,包含四个值:{top,left,bottom,right}。当你引入一个inset时,你改变了内容偏移的范围。例如,将contentinsettop值设置为10允许contentoffset的y值达到10。这会在可滚动区域周围引入填充。乍一看,似乎没什么用。实际上,为什么不直接增加内容大小呢?您需要避免更改滚动视图的内容大小,除非您不能。想知道为什么吗?想想一个表格视图(UItableView是UIScrollView的子类,所以它具有所有相同的属性),表格视图的可滚动区域被仔细计算以适合每个单元格。当您滚动超过表视图的第一个或最后一个单元格的边界时,表视图弹出并重置内容偏移量,因此单元格再次紧贴滚动视图的边界。当您尝试使用UIRefreshControl实现拉动刷新时会发生什么?不能在表视图的可滚动区域内放置UIRefreshControl,否则,表视图将允许用户在刷新控件中途停止滚动,并将刷新控件的顶部弹回到视图的顶部。因此,您必须将刷新控件放在可滚动区域上方。这将允许内容偏移首先被反弹回***行,而不是刷新控件。但是等等,如果你通过滚动足够远来初始化下拉刷新机制,因为表视图有一个内容插入,这将允许内容偏移将刷新控件弹出回可滚动区域。当刷新动作发起时,contentinset已经被修正,所以contentoffset的最小值包含了完整的刷新控制。刷新完成后,contentinset恢复正常,contentoffset也自适应大小。这里不需要对内容大小做数学计算。(这里可能比较难理解,建议看EGOTableViewPullRefresh等类库来理解)如何在自己的代码中使用contentinset?当键盘在屏幕上时,这有一个很好的用途:您想设置一个紧贴屏幕的用户界面。当键盘出现在屏幕上时,您将失去数百个像素的空间,键盘下方的所有内容都被遮挡。现在,滚动视图的边界没有改变,内容大小也没有改变(也不需要)。但是用户不能滚动滚动视图。考虑前面的公式:contentoffset的最大值不同于contentsize和bounds的大小。如果它们相等,则内容偏移量的最大值现在为{x:0,y:0}。现在开始技巧,将界面放入滚动视图。滚动视图的内容大小仍然与滚动视图的边界相同。当键盘出现在屏幕上时,您将内容插入的底部设置为等于键盘的高度。这允许在内容偏移量的最大值处显示滚动区域之外的区域。可视区域的顶部在滚动视图边界之外,所以它被剪掉了(虽然它在屏幕外,但没关系)。希望这能让您深入了解滚动视图的内部工作原理,您对缩放感兴趣吗?好吧,我们今天不谈它,但这里有一个有趣的小技巧:检查viewForZoomingInScrollView:方法是否返回视图的transform属性。你会再次发现滚动视图只是对UIView现有属性的巧妙使用。来源:answer_huang的博客【微博】