2010-02-07 38 views
1

我试图用重做和撤消功能来构建绘图应用程序。 我的想法是在“touchMoved”的图层中绘制线条,然后将图层保存在“touchEnded”中。iPhone:崩溃时绘制CGLayers存储在阵列

我不确定我是否正确地绘制图层,但一切正常,直到我清除了我正在绘制的图像并尝试重新绘制数组中的图层。

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 

    UITouch *touch = [touches anyObject]; 
    CGPoint currentPoint = [touch locationInView:self.view]; 

    UIGraphicsBeginImageContext(self.imageView.frame.size); 
    [self.imageView.image drawInRect:self.imageView.frame]; 

    CGContextRef context = UIGraphicsGetCurrentContext(); 
    CGContextRef myContext; 

    layerRef = CGLayerCreateWithContext(context, self.imageView.frame.size, NULL); 

    if (self.layer == nil) { 
     myContext =CGLayerGetContext(layerRef); 

     CGContextSetLineCap(myContext, kCGLineCapRound); 
     CGContextSetLineWidth(myContext, 5.0); 
     CGContextSetLineJoin(myContext, kCGLineJoinRound); 
     CGContextSetRGBStrokeColor(myContext, 1.0, 0.0, 0.0, 1.0); 

     CGContextBeginPath(myContext); 
     CGContextMoveToPoint(myContext, lastPoint.x, lastPoint.y); 
     CGContextAddLineToPoint(myContext, currentPoint.x, currentPoint.y); 
     CGContextStrokePath(myContext); 

     CGContextDrawLayerAtPoint(context, CGPointMake(00, 00),layerRef); 
     self.imageView.image = UIGraphicsGetImageFromCurrentImageContext(); 

       UIGraphicsEndImageContext(); 
     lastPoint = currentPoint;  
    } 
} 

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    if (self.layerArray != nil) { 
     NSLog(@"Saving layer"); 
     [self.layerArray addObject:[[NSValue alloc] initWithBytes:layerRef objCType:@encode(CGLayerRef)]]; 
     CGLayerRelease(layerRef); 
    } 
    NSLog(@"%d",[layerArray count]); 
} 

这里是我想在重绘层的方法。 应用崩溃当它到达的CGContextDrawLayerAtPoint()

- (IBAction)redrawViewButton:(id)sender { 
    UIGraphicsBeginImageContext(self.imageView.frame.size); 
    [self.imageView.image drawInRect:self.imageView.frame]; 

    NSValue *val = [layerArray objectAtIndex:0]; 
    CGLayerRef layerToShow; 
    [val getValue:&layerToShow];  

    CGContextRef context = CGLayerGetContext(layerToShow); 
    CGContextDrawLayerAtPoint(context, CGPointMake(00, 00),layerToShow); 

    self.imageView.image = UIGraphicsGetImageFromCurrentImageContext(); 
    UIGraphicsEndImageContext(); 
} 

+0

嗨@Oscar,你的方法成功了吗? – Ranjit 2012-07-12 10:58:04

回答

2

我认为layerRef是你已经映射到self.layer的伊娃吗?您似乎在访问者之间移动并直接访问伊娃,这非常容易混淆,并且容易出错。确保始终通过访问者访问您的ivars。这对于节省内存管理方面的麻烦会有很大的帮助。你会实现layerproperty是这样的:

@property (nonatomic, readwrite, retain) CGLayerRef layer; 

@synthesize layer = _layer; 

- (void)setLayer:(CGLayer)aLayer 
{ 
    CGLayerRetain(aLayer); 
    CGLayerRelease(_layer); 
    _layer = aLayer; 
} 

... 

CGLayerRef layer = CGLayerCreateWithContext(context, self.imageView.frame.size, NULL); 
self.layer = layer; 
CGLayerRelease(layer); 

这样做的关键是把所有的伊娃的内存管理的setLayer:内。 ivar访问崩溃的最常见原因是您管理不善。访问者可以保护您免受此类侵害

的其他值得注意的点A copule:

  • 没有它,如果它停留在上下文中立即设置为nil,从不释放的东西。在你的情况下,你释放layerRef,但你不清除伊娃。这意味着如果你得到touchesEnded:再次获得touchesMoved:之前,你会双击释放图层。这可能是你问题的真正原因。访问者保护你免受此影响。

  • Your touchesMoved:代码似乎很错误。每次移动时都会创建一个新图层。单个touchesEnd:可以获得几十个touchesMoved:。或者根本没有touchesMoved:。我想你的意思是把这个代码放在touchesBegan:

2

一些随机的东西:

有在touchedEnded:withEvent:内存泄漏,要添加一个保留对象self.llayerArray但从未释放阵列后,还保留了它。试试这个:

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 
    if (self.layerArray != nil) { 
     NSLog(@"Saving layer"); 
     [self.layerArray addObject: [NSValue valueWithPointer: layerRef]]; 
     CGLayerRelease(layerRef); 
    } 
    NSLog(@"%d",[layerArray count]); 
} 

A CGLayerRef是一个指针。这意味着,在redrawViewButton:可以的simpy做到这一点:

CGLayerRef* layerToShow = (CGLayerRef) [[layerArray objectAtIndex: 0] pointerValue]; 
1

最简单的解释是,无论是层或上下文格式不正确。你在使用之前都测试两个零。IIRC,如果使用“Print Description to Console”上下文菜单,调试器可以显示Core Graphic结构的值。

也许无关,但我会建议改变......

CGPointMake(00, 00) 

...到:

CGPointMake(0.0f, 0.0f) 

只是为了确保。

无论如何,我认为你需要放弃这种实现撤消的方法。它看起来简单而整洁,但实际上它会变得繁琐,复杂和不可靠。

撤消和重做是数据模型的正确功能,而不是视图或其控制器。您应该保存用户输入,然后从该数据中进行绘制,而不是保存用户输入的结果(即绘图)。

在这种情况下,您可以存储触摸的点数,触摸的时间/顺序以及任何相关的操作。视图和视图控制器根本没有“内存”。他们只需绘制目前需要绘制的数据模型即可。您将在数据模型中实现撤消和重做。要撤消你将所有的数据绘制到撤销点。要重做,请绘制最后的数据。

虽然学习曲线陡峭,但核心数据对此非常有用。它会自动实现撤消和重做。如果你的数据模型比较简单,你可以用一个存储一个自定义类的数组来实现它,该自定义类用于存储单个绘图事件的数据。

如果您尝试在视图或视图控制器中执行此操作,则最终会出现易碎代码的怪物球。