理解Frame

2024-06-23 23:58
文章标签 理解 frame

本文主要是介绍理解Frame,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转自  http://www.cocoachina.com/applenews/devnews/2013/1209/7498.html


本文由  M igrant 翻译自  Understanding Frame
Frame是布局的核心。每个开发者都使用frame定位和改变UIView和CALayer的大小。在本文中我将把焦点集中在CALayer上,因为它是UIView的底层实现,view.frame简单的返回了view.layer.frame。此外,我不会讨论setFrame:方法。虽然看起来范围十分有限,但实际上有许多有趣的事情在平凡又古老的frame getter方法中发生。
Frame依赖于什么
众所周知,frame是一个派生属性,实际上它基于一些其他的属性。实际上在计算frame值的时候会参考4个(!)属性:bounds,anchorPoint,transform,和position。
我们从bounds开始。bounds很棘手,它混合了层的内部和外部。bounds.size定义了层本身的面积,声明了它所存在的区域。设置masksToBounds为YES会把所有子层超出bounds范围的部分裁掉。另一方面,bounds的origin属性并不影响层本身的布局;然而它会影响它内部的子层的布局方式。bounds.origin定义了层内部坐标系的原点。
这里有一个例子展示了bounds.origin如何工作。例如我们定义bounds.origin为CGPointMake (20.0f, 30.0f)
如何定义本地坐标系?只要把层的左上角放到bounds.origin上就行了。
anchorPoint是一个稍微有点不同的讨厌鬼。首先,它的值标准化为0.0-1.0的范围内。获得以”点”为单位的值需要用bounds.size乘以标准化的值。更重要的是,anchorPoint定义了应用变换的坐标系的原点。
变换具有相同bounds但有不同anchorPoint的层(蓝色)会有很大区别(灰色)。
position是最简单的一个概念。它定义了经过bounds.size,anchorPoint和transform的混合后,添加到层中的最终位置。
精度的快速讨论
在写这篇博客的时候,我留意到有时我的计算结果和CoreAnimation返回的计算结果相比有所出入。有可能是我计算错误或者有精度问题。我理所当然的首先检查了精度问题。幸运的是我的直觉是正确的。CGFloat在32位架构上是一个float的类型定义(在64位架构上是double),而似乎CoreAnimation并没有理会CGFloat的实际类型而在内部直接使用了double。
要证实这个猜测并不困难。使用Hooper工具检查CALayer的framegetter方法的执行内容,我发现了一个叫做mat4_apply_to_rect的函数。然后我在这里设置了一个符号断点,实际上也就是在CA::Mat4Impl::mat4_apply_to_rect(double const*, double*)和CA::Mat4Impl::mat4_apply_to_rect(float const*, float*)上分别设置了一个断点,以确定哪一个函数被执行。当在设备上运行代码的时候,断点停在了参数是double的函数中,即使使用的是32位ARM架构的iPhone。
在一些极端情况下,使用float和double的差异是显而易见的。然而因为我们的目标是对CoreAnimation进行逆向工程并得到完全相同的结果,所以我们也使用double。我们定义一些和CoreGraphics中相同的非常简单的结构体。
 
  1. typedef struct MCSDoublePoint { 
  2.   double x, y; 
  3. } MCSDoublePoint; 
  4.   
  5. typedef struct MCSDoubleSize { 
  6.   double width, height; 
  7. } MCSDoubleSize; 
  8.   
  9. typedef struct MCSDoubleRect { 
  10.   MCSDoublePoint origin; 
  11.   MCSDoubleSize size; 
  12. } MCSDoubleRect; 
值得注意的是在64位iOS设备上,我们精心构建的struct会变得多余,因为在该架构上,CGPoint,CGSize和CGRect本来就是用doubles的。
变换
在深入分析frame之前,我们先了解一下变换。虽然CALayer使用的是一个完整的4×4的矩阵模拟CATransform3D,但它对计算frame的目的真的没有影响。所以,我们把焦点集中在CGAffineTransform上,它可以用每个人都喜欢的CATransform3DGetAffineTransform方法从CATransform3D中简单获得。
让我们从点开始,使用仿射变换来变换点是入门级的袋鼠:
 
  1. MCSDoublePoint MCSDoublePointApplyTransform(MCSDoublePoint point, CGAffineTransform t) 
  2.   MCSDoublePoint p; 
  3.   p.x = (double)t.a * point.x + (double)t.c * point.y + t.tx; 
  4.   p.y = (double)t.b * point.x + (double)t.d * point.y + t.ty; 
  5.   return p; 
上面的代码实现基于CGPointApplyAffineTransform,从根本上来讲是一个3x3的变换矩阵乘一个三维向量。
这个矩阵被CGAffineTransform的值填充,被乘的向量由点的x坐标,y坐标和1.0组成,让结果向量从矩阵中也得到转换过的元素。
通过点变换,我们很容易变换矩形。通过变换矩形的顶点并用直线连接它们创建一个平行四边形(通常可以是任意四边形)。 但这并不是CGRectApplyAffineTransform的如何工作的。这个函数接收一个CGRect参数并返回一个CGRect。正如头文件CGAffineTransform.h中的注释声明的:
通常来说因为仿射变换并不保护矩形,这个函数返回一个最小的包括经过变换的rect的四个顶点的矩形。
读过这个以后,使用double再现CGRectApplyAffineTransform变得相对直接:
 
  1. MCSDoubleRect MCSDoubleRectApplyTransform(MCSDoubleRect rect, CGAffineTransform transform) 
  2.   double xMin = rect.origin.x; 
  3.   double xMax = rect.origin.x + rect.size.width; 
  4.   double yMin = rect.origin.y; 
  5.   double yMax = rect.origin.y + rect.size.height; 
  6.   
  7.   MCSDoublePoint points[4] = { 
  8.     [0] = MCSDoublePointApplyTransform((MCSDoublePoint){xMin, yMin}, transform), 
  9.     [1] = MCSDoublePointApplyTransform((MCSDoublePoint){xMin, yMax}, transform), 
  10.     [2] = MCSDoublePointApplyTransform((MCSDoublePoint){xMax, yMin}, transform), 
  11.     [3] = MCSDoublePointApplyTransform((MCSDoublePoint){xMax, yMax}, transform), 
  12.   }; 
  13.   
  14.   double newXMin =  INFINITY; 
  15.   double newXMax = -INFINITY; 
  16.   double newYMin =  INFINITY; 
  17.   double newYMax = -INFINITY; 
  18.   
  19.   for (int i = 0; i < 4; i++) { 
  20.     newXMax = MAX(newXMax, points[i].x); 
  21.     newYMax = MAX(newYMax, points[i].y); 
  22.     newXMin = MIN(newXMin, points[i].x); 
  23.     newYMin = MIN(newYMin, points[i].y); 
  24.   } 
  25.   
  26.   MCSDoubleRect result = {newXMin, newYMin, newXMax - newXMin, newYMax - newYMin}; 
  27.   
  28.   return result; 
我们计算了四个顶点的坐标,变换它们并且得到x和y的极值。
计算Frame
我们付出努力去理解我们关心的每一件事,现在,获得frame会是很热闹:
定义一个面积为bounds.size的矩形
计算该矩形内的anchorPoint位置
将矩形放入坐标系内,anchorPoint作为坐标系的原点
应用任何你实施的变换,保持一个”包含了经过转换的顶点的最小矩形”
根据position移动anchorPoint
灰色的就是结果矩形
实现这些操作的代码如下:
 
  1. - (CGRect)frameWithBounds:(CGRect)bounds anchorPoint:(CGPoint)anchorPoint transform:(CATransform3D)transform position:(CGPoint)position 
  2.   MCSDoubleRect rect; 
  3.   
  4.   rect.size.width = bounds.size.width; 
  5.   rect.size.height = bounds.size.height; 
  6.   rect.origin.x = (double)-bounds.size.width * anchorPoint.x; 
  7.   rect.origin.y = (double)-bounds.size.height * anchorPoint.y; 
  8.   
  9.   rect = MCSDoubleRectApplyTransform(rect, CATransform3DGetAffineTransform(transform)); 
  10.   
  11.   rect.origin.x += position.x; 
  12.   rect.origin.y += position.y; 
  13.   
  14.   return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); 
虽然代码不多,但利用了我们讨论过的所有概念。
这些如何映射到UIView
关于framegetter方法,bounds和center,UIView并没有做什么工作;它只是简单的各自调用它底层的CALayer的frame,bounds和position方法。
注意center到position的映射 — 改变底层layer的anchorPoint会使center不能正确的对应到层的”中心”或者层的边界矩形的”中点”。

这篇关于理解Frame的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1088652

相关文章

一文带你理解Python中import机制与importlib的妙用

《一文带你理解Python中import机制与importlib的妙用》在Python编程的世界里,import语句是开发者最常用的工具之一,它就像一把钥匙,打开了通往各种功能和库的大门,下面就跟随小... 目录一、python import机制概述1.1 import语句的基本用法1.2 模块缓存机制1.

深入理解C语言的void*

《深入理解C语言的void*》本文主要介绍了C语言的void*,包括它的任意性、编译器对void*的类型检查以及需要显式类型转换的规则,具有一定的参考价值,感兴趣的可以了解一下... 目录一、void* 的类型任意性二、编译器对 void* 的类型检查三、需要显式类型转换占用的字节四、总结一、void* 的

深入理解Redis大key的危害及解决方案

《深入理解Redis大key的危害及解决方案》本文主要介绍了深入理解Redis大key的危害及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着... 目录一、背景二、什么是大key三、大key评价标准四、大key 产生的原因与场景五、大key影响与危

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是