65.9K
CodeProject 正在变化。 阅读更多。
Home

iOS 技巧: 带有附加子层的 UIView - 应用主层使用的相同隐式动画规则

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.47/5 (9投票s)

2014年5月20日

CPOL

2分钟阅读

viewsIcon

24621

downloadIcon

123

当你向UIView添加额外的图层时,这些新图层的动画效果与UIView的底层图层不同。我有一个技巧可以很好地解决这个问题。

引言

当你向UIView添加额外的图层时,这些新图层的动画效果与UIView的底层图层不同。我有一个技巧可以很好地解决这个问题。

背景

隐式动画是在更改视图或图层的属性时自动发生的动画。

每个UIView都有一个主/底层CALayer。当CALayer的属性发生变化时,它会查询其delegateUIView)以获取用于该更改的动画(actionForLayer:forKey:),并且由于视图是其图层的委托,因此图层会查询其视图以获取任何所需的自定义动画操作。视图管理其所有图层的隐式操作。

对于普通的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示例中,_yellowLayerlayoutSubviews中与视图的框架对齐,但由于大小更改而重新布局视图时,通常会发生动画。

但是,通过为图层分配委托……

_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

历史

  • 初始版本
© . All rights reserved.