iOS技巧:带有附加层的UIView使用了与主层相同的隐式激活规则

标签: ios UIView
发布时间: 2015/1/14 15:42:22

简介

当你为UIView添加子层时,这些层不像UIView 的后台层那样是激活的。我有一个修复它的技巧。

背景

隐式激活是改变视图或者层属性的时候自动触发的行为。
每一个 UIView 都有一个主/后台 CALayer 。当一个 CALayer 属性改变时,它会为处理该变动(actionForLayer:forKey:)而查询它的delegate(UIView) ,同时由于视图是该层的代理,图层为任何客户可能激活的行为请求它的视图。视图控制它所有图层所实现的行为。
所有过程均通过常用的UIView -> CALayer 关系自动执行。然而,如果你添加了额外的图层到现有视图,那么新图层不会设置有任何delegate  。那为什么不只把新视图设置到它从属的视图呢,你一定会这么问吧!不幸的是,这样做并不起作用,因为一个图层通过代理来控制着他拥有的视图,并且如果视图没有任何层控制时,会出现很奇怪的状况。
使用从属的视图时正确的思维习惯;
事实上,如果你只发送一个新图层的actionForLayer:forKey: 消息到视图,那么这个层将能够正常实现激活,同时视图不会被新加的图层影响。
为了展示上述描述的情况,我创建了一个座位图层代理的类。

样例类

LGLayerActionsForwarder.h

#import <Foundation/Foundation.h>

@interface LGLayerActionsForwarder : UIView

- (instancetype) initWithView: (UIView *) view;

@property (nonatomic, readonly) UIView *view;

@end

LGLayerActionsForwarder.m

#import "LGLayerActionsForwarder.h"

@implementation LGLayerActionsForwarder
{
    __weak UIView *_view;
}

- (instancetype) initWithView: (UIView *) view
{
    self = [super init];
    if (!self) return nil;

    _view = view;

    return self;
}

- (id <CAAction>) actionForLayer: (CALayer *) layer forKey: (NSString *) event
{
    return [_view actionForLayer: layer forKey: event];
}

@end

如何使用该类

创建一个LGLayerActionsForwarder  类的实例,把它赋给你创建的所有图层。

_actionsForwarder = [[LGLayerActionsForwarder alloc] initWithView: self];

_yellowLayer = [CALayer layer];
_yellowLayer.delegate = _actionsForwarder;

在CustomViewLayer  示例中yellowLayer 是和视图frame的layoutSubviews对齐的,但是由于大小发生了变化,当视图重新加载时,通常情况的动画将不会出现。
但是如果向该图层指派一个代理后...

_yellowLayer.delegate = _actionsForwarder;

...图层会在改变时终止动画。且表现出一致性,就像视图的后台层一样。后台层只会在激活范围内有效。([UIView animateWithDuration:animations:])

@implementation CustomViewWithLayer
{
    CALayer *_yellowLayer;
    LGLayerActionsForwarder *_actionsForwarder;
}

- (id) initWithCoder: (NSCoder *) coder 
{
    self = [super initWithCoder: coder];
    if (!self) return nil;

    // create a forwarder instance and link it to the view
    // NOTE: If you comment out just this line, then the yellow layer would function as a normal layer (animating).
    _actionsForwarder = [[LGLayerActionsForwarder alloc] initWithView: self];

    _yellowLayer = [CALayer layer];
    _yellowLayer.borderColor = [UIColor blueColor].CGColor;
    _yellowLayer.borderWidth = 1;
    _yellowLayer.cornerRadius = 20;
    _yellowLayer.delegate = _actionsForwarder;
    _yellowLayer.backgroundColor = [UIColor yellowColor].CGColor;

    [self.layer addSublayer: _yellowLayer];

    return self;
}

- (void) layoutSubviews
{
    [super layoutSubviews];

    _yellowLayer.frame = self.bounds;
}

@end
赞助商