【发布时间】:2011-09-26 17:06:36
【问题描述】:
如何将CALayer sublayer 放在所有子层的前面,类似于-[UIView bringSubviewToFront]?
【问题讨论】:
标签: iphone objective-c ios core-animation calayer
如何将CALayer sublayer 放在所有子层的前面,类似于-[UIView bringSubviewToFront]?
【问题讨论】:
标签: iphone objective-c ios core-animation calayer
Swift 4 版本。
想法层本身有bringToFront 和sendToBack 方法。
#if os(iOS)
import UIKit
#elseif os(OSX)
import AppKit
#endif
extension CALayer {
func bringToFront() {
guard let sLayer = superlayer else {
return
}
removeFromSuperlayer()
sLayer.insertSublayer(self, at: UInt32(sLayer.sublayers?.count ?? 0))
}
func sendToBack() {
guard let sLayer = superlayer else {
return
}
removeFromSuperlayer()
sLayer.insertSublayer(self, at: 0)
}
}
用法:
let view = NSView(frame: ...)
view.wantsLayer = true
view.layer?.backgroundColor = NSColor.gray.cgColor
let l1 = CALayer(...)
let l2 = CALayer(...)
view.layer?.addSublayer(l1)
view.layer?.addSublayer(l2)
l1.bringToFront()
【讨论】:
像这样创建一个 CALayer 类别:
@interface CALayer (Utils)
- (void)bringSublayerToFront;
@end
@implementation CALayer (Utils)
- (void)bringSublayerToFront {
CGFloat maxZPosition = 0; // The higher the value, the closer it is to the front. By default is 0.
for (CALayer *layer in self.superlayer.sublayers) {
maxZPosition = (layer.zPosition > maxZPosition) ? layer.zPosition : maxZPosition;
}
self.zPosition = maxZPosition + 1;
}
@end
【讨论】:
您可以在 CALayer 上的类别中实现此功能,如下所示:
CALayer+Extension.h
#import <QuartzCore/QuartzCore.h>
typedef void (^ActionsBlock)(void);
@interface CALayer (Extension)
+ (void)performWithoutAnimation:(ActionsBlock)actionsWithoutAnimation;
- (void)bringSublayerToFront:(CALayer *)layer;
@end
CALayer+Extension.m
#import "CALayer+Extension.h"
@implementation CALayer (Extension)
+ (void)performWithoutAnimation:(ActionsBlock)actionsWithoutAnimation
{
if (actionsWithoutAnimation)
{
// Wrap actions in a transaction block to avoid implicit animations.
[CATransaction begin];
[CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
actionsWithoutAnimation();
[CATransaction commit];
}
}
- (void)bringSublayerToFront:(CALayer *)layer
{
// Bring to front only if already in this layer's hierarchy.
if ([layer superlayer] == self)
{
[CALayer performWithoutAnimation:^{
// Add 'layer' to the end of the receiver's sublayers array.
// If 'layer' already has a superlayer, it will be removed before being added.
[self addSublayer:layer];
}];
}
}
@end
为了方便访问,您可以在项目的 Prefix.pch(预编译头文件)文件中#import "CALayer+Extension.h"。
【讨论】:
这是@MattDiPasquale 实现的变体,更准确地反映了 UIView 的逻辑:
- (void) bringSublayerToFront:(CALayer *)layer
{
[layer removeFromSuperlayer];
[self insertSublayer:layer atIndex:[self.sublayers count]];
}
- (void) sendSublayerToBack:(CALayer *)layer
{
[layer removeFromSuperlayer];
[self insertSublayer:layer atIndex:0];
}
注意:如果您不使用 ARC,您可能希望在两个函数的顶部添加 [layer retain] 并在底部添加 [layer release] 以确保 layer 不会被意外破坏保留计数 = 1 的案例。
【讨论】:
removeFromSuperlayer 方法是不必要的,因为它会在调用insertSublayer 时自动删除。
我很好奇为什么这些答案都没有提到 CALayer 上的 zPosition 属性。 Core Animation 会查看这个属性来确定图层的渲染顺序。值越高,越靠近前面。只要您的 zPosition 为 0,这些答案都有效,但要轻松地将层置于前面,请将其 zPosition 值设置为高于所有其他子层。
【讨论】:
这是正确的代码:
-(void)bringSubLayerToFront:(CALayer*)layer
{
[layer.superLayer addSubLayer:layer];
}
-(void)sendSubLayerToBack:(CALayer*)layer
{
[layer.superlayer insertSublayer:layer atIndex:0];
}
【讨论】:
这里有正确的代码
- (void)bringSublayerToFront:(CALayer *)layer {
CALayer *superlayer = layer.superlayer;
[layer removeFromSuperlayer];
[superlayer insertSublayer:layer atIndex:[superlayer.sublayers count]];
}
- (void)sendSublayerToBack:(CALayer *)layer {
CALayer *superlayer = layer.superlayer;
[layer removeFromSuperlayer];
[superlayer insertSublayer:layer atIndex:0];
}
【讨论】:
我不认为这样的方法存在,但很容易自己动手。
// CALayer+Additions.h
@interface CALayer (Additions)
- (void)bringSublayerToFront:(CALayer *)layer;
- (void)sendSublayerToBack:(CALayer *)layer;
@end
// CALayer+Additions.m
@implementation CALayer (Additions)
- (void)bringSublayerToFront:(CALayer *)layer {
CALayer *superlayer = self.superlayer;
[self removeFromSuperlayer];
[superlayer insertSublayer:gradientLayer atIndex:[self.sublayers count]-1];
}
- (void)sendSublayerToBack:(CALayer *)layer {
CALayer *superlayer = self.superlayer;
[self removeFromSuperlayer];
[superlayer insertSublayer:gradientLayer atIndex:0];
}
【讨论】: