在了解锚点之前,需要了解 UIView 三个属性:frame,bounds,center
“这三个属性太简单了吧?”
假设 view2 是 view1 的子视图,view1 设置 bounds.origin 为 (-30, -30),那么 view2 视觉上会向右、下方各移动 30,view1 位置不变。如果这个例子理解了,就差不多了,后面也会解释。
PS:这三个属性,CALayer 对应地叫做:frame,bounds,position。UIView 提供的是他们的 getter、setter,后文都用 CALayer 的属性名。
frame:origin 和 size 分别描述 在父图层坐标系下的坐标 和 图层大小(是给外部看的)
bounds:origin 描述图层左上角在内部坐标系下的坐标;size 描述内部坐标系下 图层的大小(是给内部用的)
position:锚点 anchorPoint 在父图层坐标系下的坐标(UIView 中叫 center)
anchorPoint:可以理解为图层的“把柄”,是一个点,用的是单位坐标,默认值(0.5,0.5)
这些属性平时感觉挺简单的,为什么解释的那么复杂呢?通过下面几个例子来看。
如图,frame 描述了图层在父图层坐标系中的位置、大小,很好理解
bounds 指定图层左上角在自己的坐标系中的坐标为 (0,0),以及图层大小,也很好理解
anchorPoint 是默认值 (0.5, 0.5),也就是图层中心点
position(center)是 anchorPoint 在父图层坐标系中的坐标,(30, 35)
先理解一下锚点作为 把柄 的思想:此时如果只修改 position 属性值,由于 anchorPoint 没变,仍代表中心点,那么图层肯定会随着中心点的坐标变化而平移,即在父图层上的位置随着 position 改变。(frame 是计算属性,此时读取 frame 肯定也更新了)
那么如果我修改了锚点的值呢?也就是把柄的位置变了,由于锚点的 position 没变,也就是锚点的绝对位置(在父图层坐标系的坐标)不变,看下图,锚点设置为 (0, 0),也就是图层的左上角是把柄,position 没变,把柄必须放在 position 位置,最终结果是,图层必须调整自己的位置,适应新锚点。
![](https://i-blog.csdnimg.cn/blog_migrate/a762e94aea5f384d60e6649ffc9be9b4.png)
回到前面提的问题,view1 设置了 bounds.origin,表示图层左上角在内部坐标系中的坐标是(-30,-30),那(0,0)点肯定右下移动了 30,左上角不再是(0,0)。
view2 的位置变化就有两种理解方式了:浅层的理解是,view2 的 frame 没变,(0,0)点位置变了,肯定也“跟过去”;实际是因为 view2 的 position 没变,仍为 00,把柄被抓住向右下移动 30
再强调一下,frame 由 bounds,position(anchorPoint),transform 及其他属性* 计算得出,transform 以后会详细介绍,下面只是一个例子:视图在设置 transform 旋转后,frame 的大小将代表覆盖整个矩形区域的大小,但 bounds 保持不变,frame 的宽高和 bounds 的宽高可能不再一致了。
刚接触这方面的可能就懵了,再来一个例子:视图设置 transform 变小 0.6 倍,frame 和 bounds 的区别:
bounds 的宽是 375,frame 的宽是 225!本文最开始已经介绍,frame 更像是暴露给外部用的,告诉同级视图自己的位置、大小;bounds 像是内部布局使用。
内部图层使用正常的 bounds 进行布局,最后当前视图施加 transform,对当前渲染结果“截图”后缩放,形成最终显示的内容,新的 frame 也可以计算得出了。
*其他属性:后面系列的文章会详细介绍 transform、alignmentRectInsets 等影响 frame 计算结果的属性。
锚点有什么用呢?
为什么要通过设置锚点改变图层的位置,我用 frame 不简单吗?其实这个主要用于动画,有个钟表 demo。
我们有3张表针的图片,要让图片旋转也很容易,一个属性就可以搞定(transform,以后会细说),关键是,它转的时候,是以中心点为圆心转圈对吧?怎么样让他以表针的一端为中心转呢?诶,为什么会以中心点为圆心?(这里重点在锚点,旋转、transform 先不急)
为什么锚点就是旋转时的圆心呢?其实不仅在旋转时,在设置任意 transfrom、手动设置 bounds.size 等情况 都与锚点有关。简单的例子,比如 bounds 的宽增大了40,因为 [ position 没变 且 锚点没变 ] 那么 锚点在父图层的坐标不变,为了维持这个特性,该图层只能以锚点为中心,左右各扩张 20,来吸收共 40 点变化,最后 frame.x 也只减少 20。旋转时,如果要保持 position 不变 + 锚点不变,唯一能实现的办法是,以锚点为圆心!!!
一定要理解 锚点 anchorPoint 和 锚点(在父图层)的坐标 position 这两个概念,对于以后文章非常重要
下图是用了默认的锚点的效果,如果设置锚点在 (0.5, 0.9),即图层偏下的位置,想象一下,为了保持 position 不变,图层只能上移。旋转时,上面已经论提过了,会以锚点为圆心。
下面是 demo 代码,随便一看就行了,前面的概念更重要
class ViewController: UIViewController {
// 这里为了只展示核心逻辑,视图都用的 storyboard
// 表盘图片
@IBOutlet weak var board: UIImageView!
// 分针图片
@IBOutlet weak var minutePointer: UIImageView!
// 时针图片
@IBOutlet weak var hourPointer: UIImageView!
// 秒针图片
@IBOutlet weak var secondPointer: UIImageView!
weak var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
// 下面三行可以先不写,试试效果,再加上
hourPointer.layer.anchorPoint = CGPoint(x: 0.5, y: 0.95)
minutePointer.layer.anchorPoint = CGPoint(x: 0.5, y: 0.95)
secondPointer.layer.anchorPoint = CGPoint(x: 0.5, y: 0.95)
// 每秒触发一次,改指针旋转角度
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(tick), userInfo: nil, repeats: true)
timer!.fire()
}
@objc func tick() {
let (hour, minute, second) = time
let hAngle = CGFloat.pi*CGFloat(hour)/6
let mAngle = CGFloat.pi*CGFloat(minute)/30
let sAngle = CGFloat.pi*CGFloat(second)/30
minutePointer.transform = CGAffineTransform(rotationAngle: mAngle)
secondPointer.transform = CGAffineTransform(rotationAngle: sAngle)
hourPointer.transform = CGAffineTransform(rotationAngle: hAngle)
}
// 下面 获得当前时、分、秒,不重要的代码
private var time: (Int8, Int8, Int8) {
let date = Date()
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss"
let dateStr = formatter.string(from: date)
let start2 = dateStr.index(dateStr.startIndex, offsetBy: 2)
let hour = Int8(dateStr.substring(to: start2))!
let minite = Int8(dateStr.substring(with: Range(uncheckedBounds: (dateStr.index(start2, offsetBy: 1),dateStr.index(start2, offsetBy: 3) ) )))!
let sec = Int8(dateStr.substring(with: Range(uncheckedBounds: (dateStr.index(start2, offsetBy: 4),dateStr.index(start2, offsetBy: 6) ) )))! - 1
return (hour, minite, sec)
}
}
设置锚点后 效果就正常了。
}
一定要理解 position 与 anchorPoint,以及 bounds 与 frame,不然后面到 transform 没法理解。