如何复制CALayer?

13

如何创建一个包含多个CALayer实例的NSArray(这些实例都具有相同的frame、contents等属性)?

背景:创建CALayer需要一定的开销,因此我想在一个类的init方法中创建多个CALayer(它们共享同样的属性),以便稍后在该类中使用。


为什么需要复制它们?为什么不从头开始创建它们,或者减少应用程序所需的层数? - Rog
是的,这是一个选项(从头开始创建它们),但并不是非常优雅。 - Reeds
7个回答

26

我尚未特别尝试过这个方法用于CALayer,但我知道你可以通过利用NSCoding来执行深复制:

CALayer *layer = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject:layer]];

然而我不确定复制它们如何真正有助于性能。


1
这是给你的更新。再次感谢。stackoverflow.com/a/68006776/1058199 - Alex Zavatone

6
CALayer没有内置的-(id)copy方法。我不知道为什么。但是自己编写一个并不难,只需要创建一个CALayer类别并编写自己的copy方法即可。你只需要实例化并手动从原始对象获取公共ivars/属性并设置到新的副本中。不要忘记调用[super copy]
顺便说一下,CALayer是一个对象,可以将其添加到NSArray中。

2

MaxGabriel的最佳答案的更新链接。

Objective-C

CALayer *layer1;
CALayer *layer2;

// Set up layer1's specifics.

layer2 = [NSKeyedUnarchiver unarchivedObjectOfClass:[CALayer class]
                                                            fromData:[NSKeyedArchiver archivedDataWithRootObject:layer1 requiringSecureCoding:NO error:nil] error:nil];

而且使用 Swift。

let layer1: CALayer?
var layer2: CALayer? = nil
// Set up layer1's specifics

do {
    layer2 = try NSKeyedUnarchiver.unarchivedObject(
        ofClass: CALayer.self,
        from: try NSKeyedArchiver.archivedData(withRootObject: layer1, requiringSecureCoding: false))
} catch {
// It failed.  Do something. 
}

2

非常有帮助。比最佳答案更好。 - Alex Zavatone

0

这就是为什么要使用NSProxy。您所描述的是一个常见的情况,可以从中派生出任意数量的设计模式。

《Pro Objective-C Design Patterns for iOS》提供了解决您所描述问题的方案;请阅读第3章:原型模式。以下是摘要定义:

原型模式指定使用原型实例创建对象的类型,通过复制此实例来创建新对象。


0

虽然对于你的用例来说最好使用CAReplicatorLayer
但其他人可能确实需要复制CALayer,所以我在这里写了另一个答案。
(因为CARenderer的原因,我不得不复制)

我也尝试过使用NSKeyedArchiver,它基本上可以工作,但对于复杂的CALayer,有些功能会出现问题 (对我来说,是带有滤镜的蒙版)

实现

实现非常简单。

  1. 创建新的CALayer
  2. 复制你需要/使用的属性。
  3. 对子图层和蒙版进行递归操作。

这是一个关于CAShapeLayer的示例:

extension CALayer {
    func deepCopy() -> CAShapeLayer {
        let layer = CAShapeLayer()
        layer.frame = frame
        layer.bounds = bounds
        layer.filters = filters
        layer.contents = contents
        layer.contentsScale = contentsScale
        layer.masksToBounds = masksToBounds
        //add or remove lines for your need

        if let self = self as? CAShapeLayer {
            layer.path = self.path?.copy()
            layer.lineCap = self.lineCap
            layer.lineJoin = self.lineJoin
            layer.lineWidth = self.lineWidth
            layer.fillColor = self.fillColor
            layer.strokeColor = self.strokeColor
            //add or remove lines for your need
        }

        layer.mask = mask?.deepCopy()
        layer.sublayers = sublayers?.map { $0.deepCopy() }
        return layer
    }
}

0

我在我的程序中做了完全相同的事情。

在初始化中:

    self.turrets = [NSMutableArray array];
    for (count = 0; count < kMaxTurrets; count++)
        [self spawnTurret];

生成炮塔:
evTurret* aTurret = [[[evTurret alloc] init] autorelease];
CGImageRef theImage = [self turretContents];
aTurret.contents = theImage;
double imageHeight = CGImageGetHeight(theImage);
double imageWidth = CGImageGetWidth(theImage);
double turretSize = 0.06*(mapLayer.bounds.size.width + mapLayer.bounds.size.height)/2.0;
aTurret.bounds = CGRectMake(-turretSize*0.5, turretSize*0.5, turretSize*(imageWidth/imageHeight), turretSize);
aTurret.hidden = YES;
[mapLayer addSublayer:aTurret]; 
[self.turrets addObject:aTurret];

基本上,我只是反复创建CALayer对象。这比复制它们要快,因为这种方法每个属性只需要1个CALayer调用,而复制则需要您读取属性,然后另外设置它。我使用这种方法生成大约500个对象,大约需要0.02秒,所以它绝对很快。如果您真的需要更快的速度,甚至可以缓存图像文件。

那并没有真正帮助。问题是关于复制或重复图层的。 - Alex Zavatone

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