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






4.47/5 (9投票s)
当你向UIView添加额外的图层时,这些新图层的动画效果与UIView的底层图层不同。我有一个技巧可以很好地解决这个问题。
引言
当你向UIView
添加额外的图层时,这些新图层的动画效果与UIView
的底层图层不同。我有一个技巧可以很好地解决这个问题。
背景
隐式动画是在更改视图或图层的属性时自动发生的动画。
每个UIView
都有一个主/底层CALayer
。当CALayer
的属性发生变化时,它会查询其delegate
(UIView
)以获取用于该更改的动画(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
示例中,_yellowLayer
在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
历史
- 初始版本