6

元に戻すメソッドを作成したい描画アプリがあります。描画は TouchesMoved: メソッド内で行われます。

CGContextRef を作成してスタックにプッシュするか、後で復元できるコンテキスト プロパティに保存しようとしていますが、うまくいきません。どんなアドバイスも素晴らしいでしょう。これが私が持っているものです...

UIImageView      *drawingSurface;
CGContextRef       undoContext;


- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
 UIGraphicsBeginImageContext(self.view.frame.size);
 CGContextRef context = UIGraphicsGetCurrentContext();
 [drawingSurface.image drawInRect:CGRectMake(0, 0, drawingSurface.image.size.width, drawingSurface.image.size.height)]; 
 UIGraphicsPushContext(context);

        // also tried but cant figure how to restore it
        undoContext = context;

 UIGraphicsEndImageContext();
}

次に、元に戻すボタンによってトリガーされるメソッドがあります...

- (IBAction)restoreUndoImage {
 UIGraphicsBeginImageContext(self.view.frame.size);
 UIGraphicsPopContext();
 drawingSurface.image = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
}

これを実行すると、画像内のすべてが消去されるため、drawingSurface に nil が割り当てられていると思います。

私の推測では、この方法で pop と push を使用することはできません。しかし、コンテキストを保存してから drawingSurface に戻す方法がわかりません。うーん。どんな助けでも...まあ...役に立ちます。前もって感謝します -

参考までに、画面に描画するために私が行っていることを次に示します。これはうまく機能しています。これは私のTouchesMoved内にあります:

 UIGraphicsBeginImageContext(self.view.frame.size);
 CGContextRef context = UIGraphicsGetCurrentContext();
 [drawingSurface.image drawInRect:CGRectMake(0, 0, drawingSurface.image.size.width, drawingSurface.image.size.height)]; 

 CGContextSetLineCap(context, kCGLineCapRound); //kCGLineCapSquare, kCGLineCapButt, kCGLineCapRound
 CGContextSetLineWidth(context, self.brush.size); // for size

 CGContextSetStrokeColorWithColor (context,[currentColor CGColor]);

 CGContextBeginPath(context);
 CGContextMoveToPoint(context, lastPoint.x, lastPoint.y);
 CGContextAddLineToPoint(context, currentPoint.x, currentPoint.y);
 CGContextStrokePath(context);
 drawingSurface.image = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
4

1 に答える 1

1

問題に間違った方法でアプローチし、コンテキストを混乱させていると思います。

即時モード API では、グラフィック表現ではなく、プッシュ/ポップを使用してオブジェクトの「状態」を保存します。状態は、線幅、色、位置などで構成されます。グラフィカルな表現は、ペイント操作の結果 (ビットマップ) であり、通常は保存したくないものです。

代わりに、図面の作成に使用する「情報」を保存してみてください。

私の最初の提案は、シェイプの作成とペイントを分離することです。OSX では NSBezierPath を使用できますが、iOS では点の配列を使用する必要があります。

たとえば、次のプロトコルが与えられます。

// ViewController.h
@protocol DrawSourceProtocol <NSObject>
- (NSArray*)pathsToDraw;
@end

@interface ViewController : UIViewController<DrawSourceProtocol>
@end

次の機能を実装できます。

// ViewController.m
@interface ViewController () {
  NSMutableArray *currentPath;  
  NSMutableArray *allPaths;
  MyView *view_;
}
@end

...

- (void)viewDidLoad {
  [super viewDidLoad];
  currentPath = [[NSMutableArray alloc] init];
  allPaths = [[NSMutableArray alloc] init];    
  view_ = (MyView*)self.view;
  view_.delegate = self;
}

- (NSArray*)pathsToDraw {  
  // Return the currently draw path too
  if (currentPath && currentPath.count) {
    NSMutableArray *allPathsPlusCurrent = [[NSMutableArray alloc] initWithArray:allPaths];
    [allPathsPlusCurrent addObject:currentPath];
    return allPathsPlusCurrent;
  }  
  return allPaths;
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
  currentPath = [[NSMutableArray alloc] init];
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
  // When a touch ends, save the current path
  [allPaths addObject:currentPath];
  currentPath = [[NSMutableArray alloc] init];
  [view_ setNeedsDisplay];    
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  UITouch *touch = [touches anyObject];   
  CGPoint currentPoint = [touch locationInView:self.view];

  // We store the point with the help of NSValue
  [currentPath addObject:[NSValue valueWithCGPoint:currentPoint]];  

  // Update the view
  [view_ setNeedsDisplay];  
}

ビューをサブクラス化し (ここでは MyView と呼びます)、次のように実装します。

// MyView.h
#import "ViewController.h"

@protocol DrawSourceProtocol;

@interface MyView : UIView {
   __weak id<DrawSourceProtocol> delegate_; 
}
@property (weak) id<DrawSourceProtocol> delegate;
@end

// MyView.m

@synthesize delegate = delegate_;

...

- (void)drawRect:(CGRect)rect { 
  NSLog(@"Drawing!");

  // Setup a context
  CGContextRef context = UIGraphicsGetCurrentContext();   
  CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
  CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0);
  CGContextSetLineWidth(context, 2.0);

  // Get the paths
  NSArray *paths = [delegate_ pathsToDraw];

  for (NSArray *aPath in paths) {
    BOOL firstPoint = TRUE;
    for (NSValue *pointValue in aPath) {
      CGPoint point = [pointValue CGPointValue];

      // Always move to the first point
      if (firstPoint) {
        CGContextMoveToPoint(context, point.x, point.y);
        firstPoint = FALSE;
        continue;
      }

      // Draw a point
      CGContextAddLineToPoint(context, point.x, point.y);
    }
  }

  // Stroke!
  CGContextStrokePath(context);
}

ここでの唯一の注意点は、setNeedsDisplay のパフォーマンスがあまり優れていないことです。setNeedsDisplayInRect: を使用することをお勧めします。 「描画された」 rect を決定する効率的な方法に関する私の最後の投稿を参照してください。

元に戻すには?元に戻す操作は、単に allPaths 配列から最後のオブジェクトをポップするだけです。この演習はお任せします :)

お役に立てれば!

于 2012-04-25T14:16:54.853 に答える