1

我正在尝试构建一个具有重做和撤消功能的绘图应用程序。我的想法是在“touchMoved”中的图层中绘制线条,然后将图层保存在“touchEnded”中。

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

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

4

3 回答 3

2

我认为 layerRef 是您已映射到 self.layer 的 ivar?您似乎在访问器和直接 ivar 访问之间移动,这非常令人困惑且容易出错。确保始终通过访问器访问您的 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);

这样做的目的是将您对 ivar 的所有内存管理放在setLayer:. ivar 访问崩溃的最常见原因是您对它的内存管理管理不善。访问器可以保护您免受这种影响。

一些其他值得注意的点:

  • 如果它留在上下文中,切勿在不立即将其设置为 nil 的情况下释放它。在您的情况下,您正在释放 layerRef,但您没有清除 ivar。这意味着如果您在获得另一个之前再次获得 touchesEnded: touchesMoved:,您将双重释放该层。这可能是您的问题的实际原因。访问器可以保护您免受此影响。

  • 您的 touchesMoved: 代码似乎非常错误。每次移动时,您都会创建一个新图层。你可以得到几十个touchesMoved:一个touchesEnd:。或者你可能根本没有touchesMoved:。我想你是想把这段代码放进去touchesBegan:吗?

于 2010-02-07T15:42:18.410 回答
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]);
}

ACGLayerRef是一个指针。这意味着redrawViewButton:您可以简单地执行以下操作:

CGLayerRef* layerToShow = (CGLayerRef) [[layerArray objectAtIndex: 0] pointerValue];
于 2010-02-07T15:46:59.400 回答
1

最简单的解释是层或上下文没有正确形成。您在使用前测试两者是否为零。IIRC,如果您使用“将描述打印到控制台”上下文菜单,调试器可以显示核心图形结构的值。

可能无关,但我建议更改...

CGPointMake(00, 00)

...至:

CGPointMake(0.0f, 0.0f)

只想确认一下。

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

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

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

尽管学习曲线陡峭,但 Core Data 非常适合这一点。它将自动为您实现撤消和重做。如果您的数据模型相对简单,您可以只使用一个数组来实现它,该数组存储一个自定义类,该类旨在存储单个绘图事件的数据。

如果你试图在视图或视图控制器中完成这一切,你最终会得到一个由脆弱代码组成的怪物球。

于 2010-02-07T15:51:15.587 回答