图层几何学
主要介绍图层内部是如何根据付图层和兄弟图层来控制位置和尺寸,还有如何管理图层的几何结构,以及它是如何被自动调整和自动布局影响的。
布局
UIView
有三个比较重要的布局属性frame
、bounds
、center
,
CALayer
对应的叫做frame
、bounds
、position
.
frame
代表了图层的外部坐标(也就是在父图层上占据的空间)
bounds
是内部坐标({0,0}是图层的左上角)
center、position
都代表了相对于父图层anchorPoint
所在的位置。
操纵视图的frame,实际上是改变视图下CALayer的frame。
锚点
图层的anchorPoint通过position来控制它的frame的位置,你可以认为anchorPoint是用来移动图层的把柄。
anchorPoint用单位坐标来描述,也就是图层的相对坐标,图层左上角是{0,0},右下角是{1,1},默认坐标是{0.5,0.5}
OC代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *secondHandView;
@property (nonatomic, strong) UIView *minuteHandView;
@property (nonatomic, strong) UIView *hourHandView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
CALayer *layer = [[CALayer alloc] init];
layer.bounds = CGRectMake(0, 0, 150, 150);
layer.position = self.view.center;
UIImage *image = [UIImage imageNamed:@"clock"];
layer.contents = (__bridge id)image.CGImage;
[self.view.layer addSublayer:layer];
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(clockRun)];
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)clockRun {
NSTimeZone *tZone = [NSTimeZone localTimeZone];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDate *currentDate = [NSDate date];
[calendar setTimeZone:tZone];
NSDateComponents *currentTime = [calendar components:NSCalendarUnitSecond|NSCalendarUnitMinute|NSCalendarUnitHour|NSCalendarUnitTimeZone fromDate:currentDate];
CGFloat angle = (M_PI * 2 / 60) * currentTime.second;
self.secondHandView.transform = CGAffineTransformMakeRotation(angle);
CGFloat minuteAngle = (M_PI * 2 / 60) * currentTime.minute;
self.minuteHandView.transform = CGAffineTransformMakeRotation(minuteAngle);
CGFloat hourAngle = (M_PI * 2 / 12) *currentTime.hour;
self.hourHandView.transform = CGAffineTransformMakeRotation(hourAngle);
}
- (UIView *)secondHandView {
if (_secondHandView == nil) {
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor redColor];
view.bounds = CGRectMake(0, 0, 1, 60);
view.center = self.view.center;
view.layer.anchorPoint = CGPointMake(0.5, 1);
[self.view addSubview:view];
_secondHandView = view;
}
return _secondHandView;
}
- (UIView *)minuteHandView {
if (_minuteHandView == nil) {
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor grayColor];
view.bounds = CGRectMake(0, 0, 2, 60);
view.center = self.view.center;
view.layer.anchorPoint = CGPointMake(0.5, 1);
[self.view addSubview:view];
_minuteHandView = view;
}
return _minuteHandView;
}
- (UIView *)hourHandView {
if (_hourHandView == nil) {
UIView *view = [[UIView alloc] init];
view.backgroundColor = [UIColor blackColor];
view.bounds = CGRectMake(0, 0, 3, 60);
view.center = self.view.center;
view.layer.anchorPoint = CGPointMake(0.5, 1);
[self.view addSubview:view];
_hourHandView = view;
}
return _hourHandView;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
Swift代码
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let layer = CALayer()
layer.bounds = CGRect(x: 0, y: 0, width: 150, height: 150)
layer.position = self.view.center
let image = UIImage(named:"clock")
layer.contents = image?.cgImage
view.layer .addSublayer(layer)
let link = CADisplayLink(target: self, selector: #selector(clockRun))
//将创建的CADisplayLink加入到主线程中
link.add(to: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
}
@objc private func clockRun() {
let tZone = NSTimeZone.local
var calendar = NSCalendar.current
let currentDate = Date()
calendar.timeZone = tZone
let currentTime = calendar.dateComponents([Calendar.Component.hour,Calendar.Component.minute,Calendar.Component.second], from: currentDate)
//设置秒针
let angle = Double(currentTime.second!) * (Double.pi * 2 / 60);
secondHandView.transform = CGAffineTransform(rotationAngle: CGFloat(angle));
//设置分针
let minuteAngle = Double (currentTime.minute!) * (Double.pi * 2.0 / 60)
minuteHandView.transform = CGAffineTransform(rotationAngle: CGFloat(minuteAngle))
//设置时针
let hourAngle = Double (currentTime.hour!) * (Double.pi * 2.0 / 12)
hourHandView.transform = CGAffineTransform(rotationAngle: CGFloat(hourAngle))
}
//秒针
lazy var secondHandView : UIView = {
let secondHandView = UIView();
secondHandView.backgroundColor = UIColor.red
secondHandView.bounds = CGRect(x: 0, y: 0, width: 1, height: 60)
//修改锚点
secondHandView.center = view.center;
secondHandView.layer.anchorPoint = CGPoint(x: 0.5, y: 1)
view.addSubview(secondHandView)
return secondHandView;
}()
lazy var minuteHandView : UIView = {
//设置分针针
let minuteHandView = UIView()
minuteHandView.backgroundColor = UIColor.gray
minuteHandView.bounds = CGRect(x: 0, y: 0, width: 2, height: 60)
//修改锚点
minuteHandView.center = view.center;
minuteHandView.layer.anchorPoint = CGPoint(x: 0.5, y: 1)
view.addSubview(minuteHandView)
return minuteHandView
}()
//时针
lazy var hourHandView : UIView = {
//设置时针
let hourHandView = UIView()
hourHandView.backgroundColor = UIColor.black
hourHandView.bounds = CGRect(x: 0, y: 0, width: 3, height: 60)
//修改锚点
hourHandView.center = view.center;
hourHandView.layer.anchorPoint = CGPoint(x: 0.5, y: 1)
view.addSubview(hourHandView)
return hourHandView
}()
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
1.png
坐标系
和视图一样,图层在图层树中也是相当于父图层按照层级关系放置,一个层级的position依赖于它的父层级的bounds,如果父图层发生的变化,它所有的子图层都会移动。
和UIView严格的二维坐标不同,CALayer存在于一个三维k空间当中。除了我们讨论过的position和anchorPoint属性外,CALayer还有两个属性zPosition和anchorPointZ,二者都是在Z轴上描述图层位置的浮点类型。
网友评论