IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    CALayer中的anchorPoint

    王龙发表于 2016-04-08 10:30:59
    love 0

    CALayer是封装在UIView中的,为什么要这样封装是为了使代码能够复用在OS X上,UIView上面的界面操作实际上都是在CALayer上完成的,只不过UIView中能够支持app中的各种事件,以及各种touch gesture的响应。在OS X编程中,类似的是NSView,支持一些在PC上才会有的特定的操作。但无论UIView还是NSView,底层的都是CALayer,这样Apple下很多界面的实现都可以统一起来。

    在iOS中,UIView封装了很多方法可以让用户直接操作界面,是用户意识不到CALayer的存在,但有时需要用户直接访问UIView的CALayer,因为有些事情在UIView中是无法实现的,比如
    1. Shadow,圆角,有色边框
    2. 3D变换
    3. 非矩形的形状
    4. mask bounds
    5. 非线性动画等

    要描绘一个view的位置信息,有frame,bounds和center;
    同样,要描绘一个layer(CALayer类型)的位置信息,用的是frame,bounds,position和anchorPoint

    在这里,view的center和layer的position意义相同,但它们之间是有关联的,你如果改变了view的frame,相应的layer的frame,position等信息也会同步改变。

    如果对一个图形进行旋转操作,那么这个图形的旋转点是按照layer的position来定位的。

    现在说说anchorPoint,anchorPoint的默认值是和position相同的,但如果改变anchorPoint的值,图片的显示位置会根据anchorPoint的值重新放置,也就是图片根据你的anchorPoint进行了位移。

    其实UIView中,如果用户直接修改frame或center值,也可以实现类似的效果,但为什么还会有一个anchorPoint呢,我们可以用下面的例子来说明anchorPoint的功能是frame或center无法替代的。

    我们的例子是实现一个时钟,里面除了表盘还会有时针,分针和秒针,图片如下

    我们先看看习惯的处理办法

    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        [self setupClockFace];
    
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
                                                      target:self
                                                    selector:@selector(tick)
                                                    userInfo:nil
                                                     repeats:YES];
    }
    
    - (void)setupClockFace
    {
        self.clockFace = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"ClockFace"]];
        self.hourHand = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"HourHand"]];
        self.minuteHand = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MinuteHand"]];
        self.secondHand = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"SecondHand"]];
    
        CGPoint center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
    
        self.clockFace.center = center;
        self.hourHand.center = center;
        self.minuteHand.center = center;
        self.secondHand.center = center;
    
        [self.view addSubview:self.clockFace];
        [self.view addSubview:self.hourHand];
        [self.view addSubview:self.minuteHand];
        [self.view addSubview:self.secondHand];
    
        self.view.backgroundColor = [UIColor lightGrayColor];
    }
    
    - (void)tick
    {
        // convert time to hours, minutes and seconds
        NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
    
        NSCalendarUnit units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
    
        NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];
    
        // calculate hour hand angle
        CGFloat hoursAngle = (components.hour / 12.0) * M_PI * 2.0;
    
        CGFloat minsAngle = (components.minute / 60.0) * M_PI * 2.0;
    
        CGFloat secsAngle = (components.second / 60.0) * M_PI * 2.0;
    
        // rotate hands
        self.hourHand.transform = CGAffineTransformMakeRotation(hoursAngle);
        self.minuteHand.transform = CGAffineTransformMakeRotation(minsAngle);
        self.secondHand.transform = CGAffineTransformMakeRotation(secsAngle);
    }
    

    这样的代码下显示出来的效果如下

    仔细看发现针的位置是在最中间,但这和我们实际生活中的习惯不是很相同,我们希望指针是按照针尾部进行旋转,我们当然可以把指针的图片做长,另外一部分使用透明的模式,但这样一是浪费存储空间,二是加载的图片更大,更浪费内存。我们为了实现这个效果,可以通过修改anchorPoint的值来实现

    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        [self setupClockFace];
    
        [self adjustClockHands];
    
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f
                                                      target:self
                                                    selector:@selector(tick)
                                                    userInfo:nil
                                                     repeats:YES];
    }
    
    - (void)adjustClockHands
    {
        self.hourHand.layer.anchorPoint = CGPointMake(0.5, 0.9);
        self.minuteHand.layer.anchorPoint = CGPointMake(0.5, 0.9);
        self.secondHand.layer.anchorPoint = CGPointMake(0.5, 0.9);
    }
    

    这样操作后,我们得到的效果就成这样了,应该就是我们需要的了

    这里有亮点需要额外注意,第一个就是anchorPoint的单位,他不是point或者pixel值,而是一个unit值,也就是相当于width或者height的比例,所以可以看到CGPointMake(0.5, 0.9)这样的写法;另一个是anchorPoint虽然改变了,但layer的position值并不受影响,在指针transform的时候,仍然是按照position的值进行旋转的(默认position和anchorPoint是相同的)



沪ICP备19023445号-2号
友情链接