iOS核心图形:如何擦除绘画

6

我正在按照这个教程制作我的涂鸦应用程序。

http://www.raywenderlich.com/18840/how-to-make-a-simple-drawing-app-with-uikit

他的橡皮擦功能使用白色绘图。但是我使用了图片背景,当我擦除时,我真的需要擦除。

我已经尝试过:

CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);

但是它没有起作用。以下是我的代码:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    _mouseSwiped = NO;
    UITouch *touch = [touches anyObject];
    _lastPoint = [touch locationInView:self.tempImageView];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    _mouseSwiped = YES;
    UITouch *touch = [touches anyObject];
    CGPoint currentPoint = [touch locationInView:self.tempImageView];

    UIGraphicsBeginImageContext(self.tempImageView.frame.size);
    [self.tempImageView.image drawInRect:CGRectMake(0, 0, self.tempImageView.frame.size.width, self.tempImageView.frame.size.height)];
    CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _lastPoint.x, _lastPoint.y);
    CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
    CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
    CGContextSetLineWidth(UIGraphicsGetCurrentContext(), _width );


    CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), _red, _green, _blue, 1.0);

    if (_isErasing) {
        CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
    }
    else {
        CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
    }

    CGContextStrokePath(UIGraphicsGetCurrentContext());
    self.tempImageView.image = UIGraphicsGetImageFromCurrentImageContext();
    [self.tempImageView setAlpha:_alpha];
    UIGraphicsEndImageContext();

    _lastPoint = currentPoint;
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    CGSize size = self.tempImageView.frame.size;

    if(!_mouseSwiped) {
        UIGraphicsBeginImageContext(self.tempImageView.frame.size);
        [self.tempImageView.image drawInRect:CGRectMake(0, 0, size.width, size.height)];
        CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
        CGContextSetLineWidth(UIGraphicsGetCurrentContext(), _width);


        CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), _red, _green, _blue, _alpha);
        CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _lastPoint.x, _lastPoint.y);
        CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), _lastPoint.x, _lastPoint.y);
        CGContextStrokePath(UIGraphicsGetCurrentContext());
        CGContextFlush(UIGraphicsGetCurrentContext());
        self.tempImageView.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }




    UIGraphicsBeginImageContext(self.drawImageView.frame.size);
    [self.drawImageView.image drawInRect:CGRectMake(0, 0, size.width, size.height) blendMode:kCGBlendModeNormal alpha:1.0];

    if (_isErasing) {
        CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
    }

    [self.tempImageView.image drawInRect:CGRectMake(0, 0, self.tempImageView.frame.size.width, self.tempImageView.frame.size.height) blendMode:kCGBlendModeNormal alpha:_alpha];


    self.drawImageView.image = UIGraphicsGetImageFromCurrentImageContext();
    self.tempImageView.image = nil;
    UIGraphicsEndImageContext();
}
5个回答

8
当擦除时,通过在触摸开始时交换tempImageView和drawImageView来解决此问题。
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    _mouseSwiped = NO;
    UITouch *touch = [touches anyObject];
    _lastPoint = [touch locationInView:self.tempImageView];

    if (_isErasing) {
        self.tempImageView.image = self.drawImageView.image;
        self.drawImageView.image = nil;
    }

}



- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

    _mouseSwiped = YES;
    UITouch *touch = [touches anyObject];
    CGPoint currentPoint = [touch locationInView:self.tempImageView];

    UIGraphicsBeginImageContext(self.tempImageView.frame.size);
    [self.tempImageView.image drawInRect:CGRectMake(0, 0, self.tempImageView.frame.size.width, self.tempImageView.frame.size.height)];
    CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _lastPoint.x, _lastPoint.y);
    CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
    CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
    CGContextSetLineWidth(UIGraphicsGetCurrentContext(), _width );


    if (_isErasing) {
        CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
    }
    else {
        CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), _red, _green, _blue, 1.0);
        CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
    }

    CGContextStrokePath(UIGraphicsGetCurrentContext());
    self.tempImageView.image = UIGraphicsGetImageFromCurrentImageContext();
    [self.tempImageView setAlpha:_alpha];
    UIGraphicsEndImageContext();

    _lastPoint = currentPoint;
}





- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

    CGSize size = self.tempImageView.frame.size;

    if(!_mouseSwiped) {
        UIGraphicsBeginImageContext(self.tempImageView.frame.size);
        [self.tempImageView.image drawInRect:CGRectMake(0, 0, size.width, size.height)];
        CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
        CGContextSetLineWidth(UIGraphicsGetCurrentContext(), _width);


        if (_isErasing) {
            CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
        }
        else {
            CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), _red, _green, _blue, _alpha);
            CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);
        }

        CGContextMoveToPoint(UIGraphicsGetCurrentContext(), _lastPoint.x, _lastPoint.y);
        CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), _lastPoint.x, _lastPoint.y);
        CGContextStrokePath(UIGraphicsGetCurrentContext());
        CGContextFlush(UIGraphicsGetCurrentContext());
        self.tempImageView.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }




    UIGraphicsBeginImageContext(self.drawImageView.frame.size);
    [self.drawImageView.image drawInRect:CGRectMake(0, 0, size.width, size.height) blendMode:kCGBlendModeNormal alpha:1.0];
    [self.tempImageView.image drawInRect:CGRectMake(0, 0, size.width, size.height) blendMode:kCGBlendModeNormal alpha:_alpha];
    self.drawImageView.image = UIGraphicsGetImageFromCurrentImageContext();
    self.tempImageView.image = nil;
    UIGraphicsEndImageContext();
}

你为什么要交换 tempImage 和 drawingImage?只是好奇吗? - user3674231
2
不要交换图像视图,而是直接在“主图像视图”上绘制清晰的图像。这就是我解决这个问题的方法。 - jforward5

3
如果有人仍然感兴趣,这就是在Swift 3中如何为我工作的方式。感谢OMGPOP的帮助。
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    swiped = false
    hideAllButtons()
    if let touch = touches.first {
        lastPoint = touch.location(in: self.view)
    }

    if isErasing {
        self.tempImageView.image = self.mainImageView.image
        self.mainImageView.image = nil
    }
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    swiped = true
    if let touch = touches.first {
        let currentPoint = touch.location(in: view)
        drawLineFrom(fromPoint: lastPoint, toPoint: currentPoint)

        lastPoint = currentPoint
    }

}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    showAllButtons()
    if !swiped {
        drawLineFrom(fromPoint: lastPoint, toPoint: lastPoint)
    }

    UIGraphicsBeginImageContext(mainImageView.frame.size)
    mainImageView.image?.draw(in: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: 1.0)
    tempImageView.image?.draw(in:  CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height), blendMode: .normal, alpha: opacity)
    mainImageView.image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    tempImageView.image = nil
}

// Draws a line between two points on the view
func drawLineFrom(fromPoint: CGPoint, toPoint: CGPoint) {
    UIGraphicsBeginImageContext(view.frame.size)
    let context = UIGraphicsGetCurrentContext()
    tempImageView.image?.draw(in: CGRect(x:0, y:0, width: view.frame.size.width, height: view.frame.size.height))

    context?.move(to: fromPoint)
    context?.addLine(to: toPoint)

    context?.setLineCap(.round)
    context?.setLineWidth(brushWidth)

    if isErasing {
        context?.setBlendMode(.clear)
    } else {
        context?.setStrokeColor(red: red, green: green, blue: blue, alpha: 1.0)
        context?.setBlendMode(.normal)
    }
    context?.strokePath()

    tempImageView.image = UIGraphicsGetImageFromCurrentImageContext()
    tempImageView.alpha = opacity
    UIGraphicsEndImageContext()

}

这个不起作用。它会出现问题,就像我的问题http://stackoverflow.com/questions/43137955/image-gets-blurry-and-zoomed-out-when-erasing一样。 - Jitendra Modi

1
我使用一个“画布”视图和以下功能来实现“擦除效果”。我的画布是透明的,在它下面,我有一个带有照片的背景UIImageView,在其上我绘制线条。 当我完成对画布的绘制并且用户想要最终的绘画时,我合并画布和背景。这样可以擦除先前绘制的线条:
private func drawStroke(context: CGContext?, touch: UITouch, width: CGFloat, color: UIColor) {
        guard let context = context else { return }
        let previousLocation = touch.previousLocation(in: self)
        let location = touch.location(in: self)

        context.setBlendMode(color == .clear ? .clear : .normal)
        context.setLineWidth(width)
        context.setStrokeColor(color.cgColor)
        context.setLineCap(.round)

        context.move(to: previousLocation)
        context.addLine(to: location)
        context.strokePath()
    }

1

以下是我让橡皮擦正常工作的方法。 在touchesEnded函数中,我使用以下代码在上下文中绘图:

//draw the main image on the context
self.mainImage.image?.draw(in: CGRect(x:0, y:0, width:self.view.frame.size.width, height:self.view.frame.size.height), blendMode: .normal, alpha: 1.0)
    if(erasing == false) {
        //draw the current stroke normally since we're not erasing
        self.tempDrawImage.image?.draw(in: CGRect(x:0, y:0, width:self.view.frame.size.width, height:self.view.frame.size.height), blendMode: .normal, alpha: opacity!)
    } else {
        //let's create a mask of what we would like to erase by using the destinationIn function which according to the docs is the following
        //R = D*Sa
        //Destination * Source_Alpha
        self.tempDrawImage.image?.draw(in: CGRect(x:0, y:0, width:self.view.frame.size.width, height:self.view.frame.size.height), blendMode: .destinationIn, alpha: 1.0)
        //Let's apply this mask to the current drawing using XOR blending which removes any drawing underneath our mask but leaves everything else alone
        self.mainImage.image?.draw(in: CGRect(x:0, y:0, width:self.view.frame.size.width, height:self.view.frame.size.height), blendMode: .xor, alpha: 1.0)
    }
    //let's apply the changes we've made to our mainImage
    self.mainImage.image = UIGraphicsGetImageFromCurrentImageContext();
    self.tempDrawImage.image = nil;

为了更好地理解它的工作原理,您可以逐个注释绘制调用以查看它们的外观。
您可能会发现一些别名问题,即在笔画边缘留下一些小碎片。要在擦除模式下绘制并去除这些问题,请关闭上下文中的抗锯齿功能。
let ctx = UIGraphicsGetCurrentContext()
    ctx?.setShouldAntialias(false)

-1
为什么不在单独的视图上绘制或绘画?
顶部视图 = 绘画 + 手势....,底部视图 = 背景图片。
擦除颜色 = ClearColor(透明)
最后,如果您想保存您的作品,可以自行合并两张图片。

正如教程所指出的那样,使用两个视图来处理不透明度要简单得多。 - OMGPOP
你可以这样做,但是双UIImageView被用来保留不透明度。当你在tempDrawImage上绘制时,不透明度被设置为1.0(完全不透明)。然而,当你将tempDrawImage与mainImage合并时,tempDrawImage的不透明度被设置为配置的值,从而给予画笔笔触我们想要的不透明度。如果你直接在mainImage上绘制,那么使用不同不透明度值绘制画笔笔触将会非常困难。 - OMGPOP

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接