iOS核心动画:CALayer bringSublayerToFront?

40

如何将一个CALayer子层移至所有子层的前面,类似于-[UIView bringSubviewToFront]方法?

8个回答

58

我很好奇为什么这些回答没有提及CALayer上的zPosition属性。核心动画会查看此属性以确定图层的渲染顺序。值越高,离前面就越近。只要您的zPosition为0,这些答案都可以工作,但是如果要轻松将一个图层置于最前面,请将其zPosition值设置为所有子图层的最高值。


我完全忘记了zPosition,这个答案是最好的。两个答案都可以,但我认为这个更简单可靠。 - Alex Reynolds
Apple文档中得知:“当在另一个子层上方或下方插入时,您只指定了子层在子层数组中的位置。图层的实际可见性主要由其zPosition属性的值确定,次要由它们在子层数组中的位置确定。” - spinacher
除了更改zPosition之外,由于某些原因,它对我不起作用。我试图将其绘制在TabBarController的TabBar顶部,并且无法弄清楚为什么它不会将其(绘制)放在其前面。 - C0D3

44

这是@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],以确保在其保留计数 = 1的情况下,layer 不会意外销毁。


2
removeFromSuperlayer 方法是不必要的,因为在调用 insertSublayer 时它会自动被移除。 - walkingbrad
1
removeFromSuperlayer: 的另一个问题是,如果该层是唯一的子层,则删除它似乎会导致子层数组被 nil。在 Swift 中,这会导致对 self.sublayers 进行可选解包时崩溃。(在 Obj-C 上可能没问题,因为 [nil count] 可能返回 0,实际上可以正常工作)。 - invalidname

9

Swift 4 版本。
该想法是图层本身具有 bringToFrontsendToBack 方法。

#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()

8

这里是正确的代码

- (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];
}

3
创建一个 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

2
你可以在 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"


2

这是正确的代码:

-(void)bringSubLayerToFront:(CALayer*)layer
{
  [layer.superLayer addSubLayer:layer];
}

-(void)sendSubLayerToBack:(CALayer*)layer
{
  [layer.superlayer insertSublayer:layer atIndex:0];
}

-6

我不认为存在这样的方法,但是自己编写很容易。

// 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];
}

有没有可能解释一下为什么有人会对这个投票反对? - Klaas
8
可能是因为代码有误。参数层未被使用。gradientLayer未定义。superlayer调用不需要,因为图层参数的superlayer是self。ivanzoid已发布了修正后的代码。 - sdsykes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接