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 への直接アクセスの間を移動しているように見えますが、これは非常に混乱し、エラーが発生しやすくなっています。常にアクセサーを介して ivar にアクセスするようにしてください。これは、メモリ管理の問題を解決するのに大いに役立ちます。次のように 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 アクセスでのクラッシュの最も一般的な原因は、ivar アクセスのメモリ管理を誤って管理したことです。アクセサーはそれからあなたを守ります。

その他の注目すべき点のまとめ:

  • コンテキストにとどまっている場合は、すぐに nil に設定せずに何かを解放しないでください。あなたの場合、layerRefを解放していますが、ivarをクリアしていません。つまり、別の を取得する前に touchesEnded: を再度取得するとtouchesMoved:、レイヤーが二重に解放されます。それがおそらくあなたの問題の実際の原因です。アクセサーはこれからあなたを守ります。

  • あなたの touchesMoved: コードは非常に間違っているようです。移動するたびに新しいレイヤーを作成しています。touchesMoved:1本で何十本も手に入る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

最も簡単な説明は、レイヤーまたはコンテキストのいずれかが適切に形成されていないことです。使用する前に、両方の nil をテストします。IIRC では、[説明をコンソールに出力] コンテキスト メニューを使用すると、デバッガはコア グラフィック構造の値を表示できます。

関係ないかもしれませんが、変更をお勧めします...

CGPointMake(00, 00)

...に:

CGPointMake(0.0f, 0.0f)

念のため。

いずれにしても、この undo の実装方法を放棄する必要があると思います。シンプルできちんとしているように見えますが、実際には扱いにくく、複雑で、信頼性が低くなります。

元に戻すとやり直しは、データモデルの適切な機能であり、ビューやコントローラーではありません。ユーザー入力の結果、つまり描画を保存する代わりに、ユーザー入力を保存してからそのデータから描画する必要があります。

この場合、タッチのポイント、タッチの時間/シーケンス、および関連する操作を保存します。ビューとビューコントローラーには「メモリ」がまったくありません。彼らは、データモデルがその時点で描画する必要があると示したものを単純に描画します。データ モデルに取り消しとやり直しを実装します。元に戻すには、元に戻すポイントまでのすべてのデータを描画します。やり直すには、最後のデータまで描きます。

学習曲線は急ですが、Core Data はこれに非常に適しています。自動的に取り消しとやり直しを実装します。データ モデルが比較的単純な場合は、単一の描画イベントのデータを格納するように設計されたカスタム クラスを格納する配列だけで実装できます。

ビューまたはビュー コントローラでこれをすべて実行しようとすると、壊れやすいコードのモンスター ボールになってしまいます。

于 2010-02-07T15:51:15.587 に答える