博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS:重识Transform和frame
阅读量:6575 次
发布时间:2019-06-24

本文共 2592 字,大约阅读时间需要 8 分钟。

关于frame

  • frame是一个复合属性,由center、bounds和transform共同计算而来。
  • transform改变,frame会受到影响,但是center和bounds不会受到影响。也就是你使用transform来缩放,bounds是不会变的。那么由center和bounds计算得到的frame是永远保持transform为identity时的状态。这也是为什么把transform设为identity后,view会回归到最初状态。

关于transform的计算

当你使用view.transform = xxx时候,它到底是怎么起作用的?首先,它是一个矩阵,使用矩阵乘法,对view的frame进行变换,得到新的变换,那么这个逻辑是怎样的?

  • 它是针对父视图坐标的。
  • 它是针对view的初始中心为坐标的:“初始”是指transform值为identity时的状态,即没有任何的缩放、平移或旋转;“中心”默认是view方块的中心,但实际是anchorPoint

那么viewA.transform = myTransform这么一段代码就等价于:

  1. 把父视图坐标系的原点移动到view的中心,计算中心坐标系的frame,得到frame1:
CGRect frame1 = CGRectMake(-originalFrame2.size.width/2.0,-originalFrame2.size.height/2.0, originalFrame2.size.width, originalFrame2.size.height);复制代码
  1. 以坐标系改变后的frame(即centerFrame)计算,使用矩阵乘法应用transform,得到frame2:CGRect frame2 = CGRectApplyAffineTransform(frame2, myTransform)

  2. 再把结果转回原父视图坐标系,得到frame3:

CGRect frame3 = CGRectMake(frame2.origin.x + CGRectGetMidX(originalFrame), frame2.origin.y + CGRectGetMidY(originalFrame),frame2.size.width, frame2.size.height);复制代码

这么做的好处便是在缩放的时候,是针对view当前位置的,这样view的原点不会改变,也就是缩放只会产生缩放的效果,而不会产生平移。假设使用父视图原点,frame为{10,20,100,100},缩放后变成{5,10,50,50},那么frame不仅变小了,也和原点更近了。

怎么计算两个frame之间的transform

给你一个view和一个目标frame,求一个transform,使得把这个transform给view后,view的frame等于目标frame。

在处理动画的时候会用到。

因为缩放会影响平移,而平移却不会影响缩放,所以先平移到中心和目标frame一致,然后缩放。

平移的距离就是两个center的差值,缩放比例的就是两个frame的边长之比。

即:

-(CGAffineTransform)transformFromRect:(CGRect)fromRect toRect:(CGRect)toRect{     CGAffineTransform moveTrans = CGAffineTransformMakeTranslation(CGRectGetMidX(toRect) - CGRectGetMidX(fromRect), CGRectGetMidY(toRect) - CGRectGetMidY(fromRect));        CGAffineTransform scaleTrans = CGAffineTransformMakeScale(toRect.size.width / fromRect.size.width, toRect.size.height / fromRect.size.height);        //右边先执行     return CGAffineTransformConcat(scaleTrans, moveTrans);}复制代码

为什么使用transform动画而不是设置frame?

如果transform里包含了旋转,那么计算出来的frame就没有意义了,因为frame总是描述一个“摆正的”方块,而旋转后的方块是没法描述的。

但对于只有平移和缩放,用上述逻辑是可以计算的。我在做一个过场动画的时候用到了这个,动画是类似系统相册那样从一个小图逐渐放大到全屏,所以你拥有的信息是一个起始的frame,以这个为开始动画。

我尝试了通过直接设置frame来执行动画,但发现效果糟糕,因为动画虽然有一个过程,但其实从动画一开始,frame就已经修改了。如果直接设置frame,那么开始的时候,子视图就会按变化后的frame来重新布局,而不是跟随父视图一起慢慢变化

动画是渲染呈现上的样子,而实际的数值却是另一种样子,在core animation里有模型树呈现树的区别。

举个例子:

测试view是灰色,它有一个子视图是红色:

-(void)layoutSubviews{    innerView.frame = CGRectMake(10, 10, self.frame.size.width-20, self.frame.size.height-20);}复制代码

内部的view保持和父视图10的边距。所以看第一个动画,在刚开始的时候,红色的view就变成了动画结束时的大小,而第二个动画使用transform变换,其实layoutSubviews并没有调用,但是却得到了想要的效果。貌似transform只是影响了view的渲染,而且是影响了整个的子视图数,就像把这个view当做一张图片一样缩小了,而内部却不需要重新布局。

使用transform效果更好,那么就要从一个初始frame计算得到transform,使得赋值给view后,它就是到初始frame的位置。所以就有了上面的transform计算。

转载地址:http://zlgjo.baihongyu.com/

你可能感兴趣的文章
五年 Web 开发者 star 的 github 整理说明
查看>>
Docker 部署 SpringBoot 项目整合 Redis 镜像做访问计数Demo
查看>>
ReactNative字体大小不随系统字体大小变化而变化
查看>>
中台之上(五):业务架构和中台的难点,都是需要反复锤炼出标准模型
查看>>
为什么中台是传统企业数字化转型的关键?
查看>>
使用模板将Web服务的结果转换为标记语言
查看>>
inno setup 打包脚本学习
查看>>
php 并发控制中的独占锁
查看>>
从pandas到geopandas
查看>>
用express搭建网站
查看>>
如何在 Swift 中进行错误处理
查看>>
[Leetcode] Factor Combinations 因数组合
查看>>
用tinypng插件创建gulp task压缩图片
查看>>
APM终端用户体验监控分析(下)
查看>>
React Native 0.20官方入门教程
查看>>
JSON for Modern C++ 3.6.0 发布
查看>>
Tomcat9.0部署iot.war(环境mysql8.0,centos7.2)
查看>>
我的友情链接
查看>>
Oracle 服务作用
查看>>
监听在微信中打开页面时的自带返回按钮事件
查看>>