2010-02-07 50 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) 

只是爲了確保。

無論如何,我認爲你需要放棄這種實現撤消的方法。它看起來簡單而整潔,但實際上它會變得繁瑣,複雜和不可靠。

撤消和重做是數據模型的正確功能,而不是視圖或其控制器。您應該保存用戶輸入,然後從該數據中進行繪製,而不是保存用戶輸入的結果(即繪圖)。

在這種情況下,您可以存儲觸摸的點數,觸摸的時間/順序以及任何相關的操作。視圖和視圖控制器根本沒有「內存」。他們只需繪製目前需要繪製的數據模型即可。您將在數據模型中實現撤消和重做。要撤消你將所有的數據繪製到撤銷點。要重做,請繪製最後的數據。

雖然學習曲線陡峭,但核心數據對此非常有用。它會自動實現撤消和重做。如果你的數據模型比較簡單,你可以用一個存儲一個自定義類的數組來實現它,該自定義類用於存儲單個繪圖事件的數據。

如果您嘗試在視圖或視圖控制器中執行此操作,則最終會出現易碎代碼的怪物球。