撤消 CGLayer 的重做问题

Posted

技术标签:

【中文标题】撤消 CGLayer 的重做问题【英文标题】:undo redo issues with CGLayer 【发布时间】:2012-07-09 12:16:04 【问题描述】:

我正在使用 CgLayer 上的 unod 重做操作,我尝试了一些代码,但无法使其正常工作,不知道我哪里出错了,下面是我编写的代码

这是我的 drawRect 函数

- (void)drawRect:(CGRect)rect
    
    m_backgroundImage = [UIImage imageNamed:@"bridge.jpg"];

    CGPoint drawingTargetPoint = CGPointMake(0,0);
    [m_backgroundImage drawAtPoint:drawingTargetPoint];


    switch(drawStep)
    
          case DRAW:
          
              CGContextRef context = UIGraphicsGetCurrentContext();

              if(myLayerRef == nil)
              

                  myLayerRef = CGLayerCreateWithContext(context, self.bounds.size, NULL);
                 

              CGContextDrawLayerAtPoint(context, CGPointZero, myLayerRef); 
              break;
                     

         case UNDO:
                     
              [curImage drawInRect:self.bounds];              
              break;
         

        default:
            break;
          

在触摸结束时,我将图层转换为 NSValue 并作为 keyValue 对存储到 NSDictionary 中,然后将字典对象添加到数组中。

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
        
    NSValue *layerCopy = [NSValue valueWithPointer:myLayerRef];


    NSDictionary *lineInfo = [NSDictionary dictionaryWithObjectsAndKeys:layerCopy, @"IMAGE",
                              nil];

    [m_pathArray addObject:lineInfo];    
    NSLog(@"%i",[m_pathArray count]);


下面是我的撤消功能

- (void)undoButtonClicked
   
    if([m_pathArray count]>0)
    
        NSMutableArray *_line=[m_pathArray lastObject];
        [m_bufferArray addObject:[_line copy]];
        [m_pathArray removeLastObject];
        drawStep = UNDO;
        [self redrawLine];
     


//Redraw functions

- (void)redrawLine

    NSDictionary *lineInfo = [m_pathArray lastObject];

    NSValue *val = [lineInfo valueForKey:@"IMAGE"];

    CGLayerRef  layerToShow = (CGLayerRef) [val pointerValue];

    CGContextRef context1 = CGLayerGetContext(layerToShow);
    CGContextDrawLayerAtPoint(context1, CGPointMake(00, 00),layerToShow);
    [self setNeedsDisplayInRect:self.bounds];

我认为这是我出错的地方。所以请朋友们帮帮我。

从下面的 cmets 中,我添加了函数,它在 Cglayer 中绘制(这个函数我正在调用 touchesMovedEvent。

- (void) drawingOperations
    
    CGContextRef context1 = CGLayerGetContext(myLayerRef);    

    CGPoint mid1 = midPoint(m_previousPoint1, m_previousPoint2); 
    CGPoint mid2 = midPoint(m_currentPoint, m_previousPoint1);     

    CGContextMoveToPoint(context1, mid1.x, mid1.y);
    CGContextAddQuadCurveToPoint(context1, m_previousPoint1.x, m_previousPoint1.y, mid2.x, mid2.y); 
    CGContextSetLineCap(context1, kCGLineCapRound);
    CGContextSetLineWidth(context1, self.lineWidth);
    CGContextSetStrokeColorWithColor(context1, self.lineColor.CGColor);           
    CGContextSetAllowsAntialiasing(context1, YES);
    CGContextSetInterpolationQuality(context1, kCGInterpolationHigh); 
    CGContextSetAlpha(context1, self.lineAlpha);
    CGContextStrokePath(context1);    

问候 兰吉特

【问题讨论】:

你没有在图层的上下文中绘制任何东西。 redrawLine 中的代码是您展示的唯一代码,它甚至可以获取图层的上下文,但它所做的只是尝试将图层绘制到自身中。那么,除了缺少任何绘图之外,您所说的这段代码不起作用是什么意思? 您好彼得,感谢您的回复。不绘制到图层的上下文中,您的意思是进入 redrawLine 函数或 Case: DRAW?。我认为您与 redrawLine 函数有关,因为在 Case:DRAW 中,我正在绘制图层的上下文。第二件事,我是否应该为撤消重做功能或其他东西存储层,我的“touchesEnded”功能是否正确?因为我尝试使用 NSLog 对其进行调试,并且每次 CgLayer 的 NSValue 都是相同的。所以我把他们弄糊涂了。 不,drawRect: 不会绘制到图层的上下文中。 drawRect: 中的 DRAW 案例将图层绘制到 UIKit 的当前上下文中 - 即到屏幕上。 redrawLine 中的代码确实绘制到层的上下文中,但它也将层绘制到该上下文中——正如我所说,将层绘制到自身中。 谢谢你的回复,那请告诉我应该怎么做? 好了,现在你有了一个绘制到图层中的方法,但是你在什么时候调用它呢?您显示的代码都没有。 【参考方案1】:

实现撤消和重做的最佳方法是实现NSUndoManager 作为它的简要描述,您不必保存要撤消或重做的对象的每个状态,NSUndoManager 本身会为您制作.. .

实现这一目标的步骤是:

1- 初始化 NSUndoManager。

2- 使用指定的函数调用在 NSUndoManager 对象中注册对象状态,稍后将为您讨论这个问题。

3-在 NSUndoManager 对象中使用撤消或重做或清除动作功能。

我的工作解决方案中的前

-在.h文件中

@property (nonatomic,retain) NSUndoManager *undoManager;

在 .m 文件中

@synthesize undoManager;

在“viewDidLoad”方法中——初始化你的 NSUndoManager

undoManager = [[NSUndoManager alloc] init];

假设你的类中有一个使用捏合放大/缩小的函数,所以在你的“viewDidLoad”中你将拥有

UIPinchGestureRecognizer *pinchGesture =
    [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
    pinchGesture.delegate = (id)self;
    [self.view addGestureRecognizer:pinchGesture];

所以捏会放大/缩小这样 请注意,“MyImageView”是我们要放大/缩小的图像

- (void)pinch:(UIPinchGestureRecognizer*)recognizer

    if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateChanged) 
        NSLog(@"gesture.scale = %f", recognizer.scale);

        CGFloat currentScale = self.MyImageView.frame.size.width / self.MyImageView.bounds.size.width;
        CGFloat newScale = currentScale * recognizer.scale;
        //Here is the line that register image to NSUndoManager before making and adjustments to the image "save current image before changing the transformation"
        //Add image function is function that fired when you Make undo action using NSUndoManager "and so we maintain only image transformation that changed when you zoom in/out"
         [[undoManager prepareWithInvocationTarget:self] AddImage:self.MyImageView.transform];

        if (newScale < 0.5) 
            newScale = 0.5;
        
        if (newScale > 5) 
            newScale = 5;
        

        CGAffineTransform transform = CGAffineTransformMakeScale(newScale, newScale);
        self.MyImageView.transform = transform;
        recognizer.scale = 1;
    

-AddImage 函数将当前图像转换状态保存在 NSUndoManager 中。

-(void)AddImage:(CGAffineTransform)sender
    CGAffineTransform transform = sender;
    self.MyImageView.transform = transform;

所以如果你有按钮可以进行撤消操作

-(IBAction)OptionsBtn:(id)sender
  if ([undoManager canUndo]) 
      [undoManager undo];
  

因此,如果您想取消所有操作,则有两种方法

while ([undoManager canUndo]) 
   [undoManager undo];

[undoManager removeAllActions];

【讨论】:

嗨 Mina Nabil,感谢您的回复,我最近了解到,从 wwdc 2012 会议开始,CgLayer 将被弃用,而应使用 CALayer。但我决定以正常方式而不是使用 CGLayer 或 CALayer。但我想实现这个 NSUndoManager,但在此之前请看一下这个链接,我有代码,我是如何实现撤消和重做的***.com/questions/11502320/… 对我来说是好的 np ,但是您是否首先了解 NSUndoManger 的概念以及它是如何工作的,如果您可以自己实现它,我有 np 为您实现它,但这里的目的是了解并分析..无论如何我会在你的CALayer代码中帮助你。 不,我得到了一部分。你检查我的链接了吗?你有什么要说的?为什么我的橡皮擦不工作?【参考方案2】:
- (void)redrawLine

    NSDictionary *lineInfo = [m_pathArray lastObject];
    NSValue *val = [lineInfo valueForKey:@"IMAGE"];
    CGContextRef context1 = (CGContextRef) [val pointerValue];
    CGContextDrawLayerAtPoint(context1, CGPointMake(00, 00),layerToShow);
    [self setNeedsDisplayInRect:self.bounds];

用你的代码更新这个方法

【讨论】:

嗨,Ron,感谢您的评论,但我已经放弃了这种使用 CGLayers 的方法

以上是关于撤消 CGLayer 的重做问题的主要内容,如果未能解决你的问题,请参考以下文章

检测 iOS 13 撤消和重做

Qt 5,为 QTabWidget 中的每个 QTextBrowser 撤消/重做

撤消/重做在 android Canvas 中不起作用

在 CoreData 应用程序上摇动撤消工作,但没有显示撤消/重做提示

在fabric.js中使用stateProperties和hasStateChanged实现撤消/重做

Flex ApplyFormatOperation 中断 Spark TextArea 中的撤消/重做