深入理解UIScrollView

前言

这篇文章是关于UIScrollView的工作原理,不涉及基础的语法和功能实现,文章主要是我的总结和实战,主要介绍了BoundsFrame属性的区别,只有理解了这两个属性才能更好的理解UIScrollView的工作方式,文章为原创,转载请注明出处

参考文章 ObjC期刊 和 Bounds和Frame的区别


  • 了解UIScrollView是怎么工作之前,需要先了解UIView,特别是视图渲染的两步

光栅化和组合

  • 光栅化:光栅化是产生一组绘图指令并且生成一张图片。比如绘制一个圆角矩形,带图片,文字居中显示的UIButton。这些图片没有马上被绘制到屏幕上,而是被自己的视图留到下一个步骤使用
  • 组合:每个图片都会产生一个光栅化图片,这些图片被一个接一个的绘制,并产生一个屏幕大小的图片
  • 图片被一个接一个的绘制到父视图上面,视图最顶层的是UIWindow,它组合好的便是我们看到的
  • 现在回想一下每个视图都有一个BoundsFrame,视图的Frame和Bounds的大小通常是一样的(但也可以不一样),但它们的origin通常是不同的,弄懂这两个是理解UIScrollView的关键,在光栅化的过程中,视图不关心自己的Frame或在图层层级中的位置,它只关心绘制自己的Content,这个绘制被发生在每个视图-drawRect方法中
  • 在-drawRect:调用前,视图会创建一个空的图片来绘制Content。这个图片的坐标系统是视图的Bounds,几乎每个视图的Bounds的Origin都是{0, 0},因此当在左上角绘制时会发生在{0, 0},在右下角绘制时会出现在{width, height},当超出了光栅化的部分将会被丢弃。

但事实上显示时只要不设置属性clipsToBounds属性为true,绘制在外面的视图还是会被显示出来

  • 在组合的过程中,每个视图将自己光栅化图片组合到自己的父视图的光栅化的图片上。Frame决定了自己在父视图的位置,Frame的Origin属性决定了视图的光栅化图片对于父坐标的偏移量。因为Bounds和Frame的大小是一样的,所以光栅化的图片是像素对齐的,这保证了光栅化的图片不会被缩小或拉伸

CompositedPositionX = View.frame.origin.x - SuperView.bounds.origin.x
CompositedPositionY = View.frame.origin.y - SuperView.bounds.origin.y

大多数视图的Bounds的Origin都是{0, 0}

CompositedPositionX = View.frame.origin.x
CompositedPositionY = View.frame.origin.y

  • 这样做是有道理的,当改变视图的Frame.origin时它会相对父坐标而移动,在父坐标外的视图会在光栅化过程时被截取,但技术上讲还是会显示出来,但那是光栅化过程之后的事了。

关于Bounds和Frame的区别

 CGRect rectForFirstView = CGRectMake(50.0, 100.0, 200.0, 200.0);
 UIView *firstView = [[UIView alloc]initWithFrame:rectForFirstView];
 firstView.backgroundColor = [UIColor blueColor];
 // [firstView setBounds:CGRectMake(20.0, 80.0, 200.0, 200.0)];
    
 CGRect rectForSecondView = CGRectMake(100.0, 100.0, 100.0, 100.0);
 UIView *secondView = [[UIView alloc]initWithFrame:rectForSecondView];
 secondView.backgroundColor = [UIColor yellowColor];
    
 [self.view addSubview:firstView];
 [firstView addSubview:secondView];

在这里插入图片描述

  • 首先注释掉改变第一个视图Bounds的代码,将第二个视图作为第一个视图的子视图,第一个视图作为View的子视图,这时两个视图的位置如上

此时添加如下代码块

 [firstView setBounds:CGRectMake(20.0, 80.0, 200.0, 200.0)];

在这里插入图片描述

  • 视图此时相对父视图的位置是这样的

父视图的Frame.origin并没有改变,但是子视图的位置却被改变了,改变父视图Bounds.origin改变后,整个视图的变化是这样的

  • 再增加如下代码
 [firstView setBounds:CGRectMake(0.0, 0.0, 300.0, 300.0)];
 NSLog(@"firstView.Bounds:%@", NSStringFromCGRect(firstView.frame));

控制台打印消息如下
firstView.Bounds: {{0, 50}, {300, 300}}

  • 可见当Bounds的Size改变的时候,整个Frame变成和Bounds同样的大小,并且改变了原点坐标

changedFrame.origin.x = (frame.origin.x - changedBounds.size.width) / 2.0
changedFrame.origin.y = (frame.origin.y - changedBounds.size.height) / 2.0


UIScrollView的Content Offset

现在所讲的跟UIScrollView有什么关系呢,考虑实现一种滚动,有一个拖动Frame不断改变的视图。这达到了同样的效果,当拖动在右边,那么改变的是Frame.origin.x,你看,这就是UIScrollView

  • UIScrollView中有很多代表性的视图,当实现平移这个功能的时候,用户移动手指,你需要时刻改变每个视图的Frames。当组合光栅化到了它父视图的什么位置,记住这个公式

CompositedPosition.x = View.frame.origin.x - SuperView.bounds.origin.x
CompositedPosition.y = View.frame.origin.y - SuperView.bounds.origin.y

  • Bounds.origin.x的值总是0,但当它不为0呢(如上文),当改变父视图的Bounds为{-30, -30}时,子视图也会随之移动。

这是UIScrollView的工作原理,当设置它的ContentOffset属性时,它实际上改变了Bounds.origin属性,实际上,ContentOffset甚至是不存在的,它的代码实现大致如下

-(void)setContentOffset:(CGPoint)offset{
	CGRect Bounds = [self bounds];
	bounds.origin = offset;
	[self setBounds: bounds];
}

当改变UIScrollView的Bounds的Origin到一定大小时,超过组合范围内的视图会消失

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值