我正在尝试使用重做和撤消功能构建绘图应用。 我的想法是在“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();
}
答案 0 :(得分: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);
重点是将{iv}的所有内存管理放在setLayer:
内。在ivar访问中崩溃的最常见原因是你对它的内存管理管理不善。访问者可以保护您免受此攻击。
其他值得注意的一点:
如果它保持在上下文中,则不要立即将其设置为nil。在你的情况下,你发布了layerRef,但你没有清除ivar。这意味着如果你获得touchesEnded:再次获得另一个touchesMoved:
之前,你将双重释放该层。这可能是你问题的真正原因。访问者可以保护您免受此攻击。
你的touchesMoved:代码似乎非常错误。每次搬家时都要创建一个新图层。对于单个touchesMoved:
,您可以获得数十个touchesEnd:
。或者你根本得不到touchesMoved:
。我想你的意思是把这段代码放在touchesBegan:
?
答案 1 :(得分: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]);
}
CGLayerRef
是一个指针。这意味着在redrawViewButton:
中你可以简单地做到这一点:
CGLayerRef* layerToShow = (CGLayerRef) [[layerArray objectAtIndex: 0] pointerValue];
答案 2 :(得分:1)
最简单的解释是图层或上下文未正确形成。你在使用前测试两者都是零。 IIRC,如果您使用“打印描述到控制台”上下文菜单,调试器可以显示核心图形结构的值。
可能不相关但我建议改变......
CGPointMake(00, 00)
...为:
CGPointMake(0.0f, 0.0f)
只是为了确保。
无论如何,我认为你需要放弃这种实现撤销的方法。它看起来简单而整洁,但实际上它会变得繁琐,复杂和不可靠。
撤消和重做是数据模型的正确功能,而不是视图或其控制器。您应该保存用户输入,然后从该数据中提取,而不是保存用户输入的结果,即图纸。
在这种情况下,您可以存储触摸点,触摸的时间/顺序以及相关的操作。视图和视图控制器根本没有“内存”。他们只需绘制当前所需绘制的数据模型。您将在数据模型中实现撤消和重做。要撤消,您可以将所有数据绘制到撤消点。要重做,您需要绘制最后的数据。
尽管学习曲线陡峭,但核心数据对此非常有利。它会自动为您实现撤消和重做。如果您的数据模型相对简单,您可以使用一个存储自定义类的数组来实现它,该类用于存储单个绘图事件的数据。如果您尝试在视图或视图控制器中执行此操作,最终会得到一个脆弱代码的怪物球。