ios Swift -为什么我的橡皮擦工具像素化

fumotvh3  于 2023-05-02  发布在  iOS
关注(0)|答案(2)|浏览(88)

在CoreGraphics中创建橡皮擦工具有很多问题。我找不到符合“像素化”的。
情况是这样的我在玩一个简单的绘画项目。钢笔工具工作正常。橡皮擦工具是可怕的像素化。以下是我的意思的屏幕截图:

下面是我使用的绘图代码(已更新):

//  DrawingView
//  
//
//  Created by David DelMonte on 12/9/16.
//  Copyright © 2016 David DelMonte. All rights reserved.
//

import UIKit

public protocol DrawingViewDelegate {
    func didBeginDrawing(view: DrawingView)
    func isDrawing(view: DrawingView)
    func didFinishDrawing(view: DrawingView)
    func didCancelDrawing(view: DrawingView)
}


open class DrawingView: UIView {

    //initial settings
    public var lineColor: UIColor = UIColor.black
    public var lineWidth: CGFloat = 10.0
    public var lineOpacity: CGFloat = 1.0
    //public var lineBlendMode: CGBlendMode = .normal

    //used for zoom actions
    public var drawingEnabled: Bool = true

    public var delegate: DrawingViewDelegate?

    private var currentPoint: CGPoint = CGPoint()
    private var previousPoint: CGPoint = CGPoint()
    private var previousPreviousPoint: CGPoint = CGPoint()

    private var pathArray: [Line] = []
    private var redoArray: [Line] = []

    var toolType: Int = 0

    let π = CGFloat(M_PI)
    private let forceSensitivity: CGFloat = 4.0

    private struct Line {
        var path: CGMutablePath
        var color: UIColor
        var width: CGFloat
        var opacity: CGFloat
        //var blendMode: CGBlendMode

        init(path : CGMutablePath, color: UIColor, width: CGFloat, opacity: CGFloat) {
            self.path = path
            self.color = color
            self.width = width
            self.opacity = opacity
            //self.blendMode = blendMode
        }
    }

    override public init(frame: CGRect) {
        super.init(frame: frame)
        self.backgroundColor = UIColor.clear
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.backgroundColor = UIColor.clear
    }

    override open func draw(_ rect: CGRect) {
        let context : CGContext = UIGraphicsGetCurrentContext()!

        for line in pathArray {
            context.setLineWidth(line.width)
            context.setAlpha(line.opacity)
            context.setLineCap(.round)

            switch toolType {
            case 0: //pen

                context.setStrokeColor(line.color.cgColor)
                context.addPath(line.path)
                context.setBlendMode(.normal)

                break

            case 1: //eraser

                context.setStrokeColor(UIColor.clear.cgColor)
                context.addPath(line.path)
                context.setBlendMode(.clear)

                break

            case 3: //multiply

                context.setStrokeColor(line.color.cgColor)
                context.addPath(line.path)
                context.setBlendMode(.multiply)

                break

            default:
                break
            }

            context.beginTransparencyLayer(auxiliaryInfo: nil)
            context.strokePath()
            context.endTransparencyLayer()
        }
    }



    override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard drawingEnabled == true else {
            return
        }

        self.delegate?.didBeginDrawing(view: self)
        if let touch = touches.first as UITouch! {
            //setTouchPoints(touch, view: self)
            previousPoint = touch.previousLocation(in: self)
            previousPreviousPoint = touch.previousLocation(in: self)
            currentPoint = touch.location(in: self)

            let newLine = Line(path: CGMutablePath(), color: self.lineColor, width: self.lineWidth, opacity: self.lineOpacity)
            newLine.path.addPath(createNewPath())
            pathArray.append(newLine)
        }
    }

    override open func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard drawingEnabled == true else {
            return
        }

        self.delegate?.isDrawing(view: self)
        if let touch = touches.first as UITouch! {
            //updateTouchPoints(touch, view: self)
            previousPreviousPoint = previousPoint
            previousPoint = touch.previousLocation(in: self)
            currentPoint = touch.location(in: self)

            let newLine = createNewPath()
            if let currentPath = pathArray.last {
                currentPath.path.addPath(newLine)
            }
        }
    }

    override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard drawingEnabled == true else {
            return
        }

    }

    override open func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard drawingEnabled == true else {
            return
        }

    }


    public func canUndo() -> Bool {
        if pathArray.count > 0 {return true}
        return false
    }

    public func canRedo() -> Bool {
        return redoArray.count > 0
    }

    public func undo() {
        if pathArray.count > 0 {

            redoArray.append(pathArray.last!)
            pathArray.removeLast()
        }

        setNeedsDisplay()
    }

    public func redo() {
        if redoArray.count > 0 {
            pathArray.append(redoArray.last!)
            redoArray.removeLast()
        }
        setNeedsDisplay()
    }

    public func clearCanvas() {
        pathArray = []
        setNeedsDisplay()
    }


    private func createNewPath() -> CGMutablePath {
        //print(#function)
        let midPoints = getMidPoints()
        let subPath = createSubPath(midPoints.0, mid2: midPoints.1)
        let newPath = addSubPathToPath(subPath)
        return newPath
    }

    private func calculateMidPoint(_ p1 : CGPoint, p2 : CGPoint) -> CGPoint {
        //print(#function)
        return CGPoint(x: (p1.x + p2.x) * 0.5, y: (p1.y + p2.y) * 0.5);
    }

    private func getMidPoints() -> (CGPoint,  CGPoint) {
        //print(#function)
        let mid1 : CGPoint = calculateMidPoint(previousPoint, p2: previousPreviousPoint)
        let mid2 : CGPoint = calculateMidPoint(currentPoint, p2: previousPoint)
        return (mid1, mid2)
    }

    private func createSubPath(_ mid1: CGPoint, mid2: CGPoint) -> CGMutablePath {
        //print(#function)
        let subpath : CGMutablePath = CGMutablePath()
        subpath.move(to: CGPoint(x: mid1.x, y: mid1.y))
        subpath.addQuadCurve(to: CGPoint(x: mid2.x, y: mid2.y), control: CGPoint(x: previousPoint.x, y: previousPoint.y))
        return subpath
    }

    private func addSubPathToPath(_ subpath: CGMutablePath) -> CGMutablePath {
        //print(#function)
        let bounds : CGRect = subpath.boundingBox

        let drawBox : CGRect = bounds.insetBy(dx: -0.54 * lineWidth, dy: -0.54 * lineWidth)
        self.setNeedsDisplay(drawBox)
        return subpath
    }
}

更新:
我注意到每一个橡皮擦触摸是正方形的。请参阅第二张图片以显示更详细的内容:

然后我按照Pranal Jaiswal的建议重写了一些代码:

override open func draw(_ rect: CGRect) {
        print(#function)
        let context : CGContext = UIGraphicsGetCurrentContext()!

        if isEraserSelected {
            for line in undoArray {
                //context.beginTransparencyLayer(auxiliaryInfo: nil)
                context.setLineWidth(line.width)
                context.addPath(line.path)
                context.setStrokeColor(UIColor.clear.cgColor)
                context.setBlendMode(.clear)
                context.setAlpha(line.opacity)
                context.setLineCap(.round)
                context.strokePath()

            }
        } else {
            for line in undoArray {
                context.setLineWidth(line.width)
                context.setLineCap(.round)
                context.addPath(line.path)
                context.setStrokeColor(line.color.cgColor)
                context.setBlendMode(.normal)
                context.setAlpha(line.opacity)
                context.strokePath()
            }

        }
    }

I'm still getting the same result. I'd appreciate any more help.
xt0899hw

xt0899hw1#

我不能完全看你的代码,但我在Swift 2中做了类似的事情。3前一段时间(我知道你在看swift 3,但现在这是我的版本)。
下面是绘图类的工作方式。

import Foundation
import UIKit
import QuartzCore

class PRSignatureView: UIView

{

var drawingColor:CGColorRef = UIColor.blackColor().CGColor //Col
var drawingThickness:CGFloat = 0.5
var drawingAlpha:CGFloat = 1.0

var isEraserSelected: Bool

private var currentPoint:CGPoint?
private var previousPoint1:CGPoint?
private var previousPoint2:CGPoint?

private var path:CGMutablePathRef = CGPathCreateMutable()

var image:UIImage?

required init?(coder aDecoder: NSCoder) {
    //self.backgroundColor = UIColor.clearColor()
    self.isEraserSelected = false
    super.init(coder: aDecoder)
    self.backgroundColor = UIColor.clearColor()
}

override func drawRect(rect: CGRect)
{
    self.isEraserSelected ? self.eraseMode() : self.drawingMode()
}

private func drawingMode()
{
    if (self.image != nil)
    {
        self.image!.drawInRect(self.bounds)
    }
    let context:CGContextRef = UIGraphicsGetCurrentContext()!
    CGContextAddPath(context, path)
    CGContextSetLineCap(context, CGLineCap.Round)
    CGContextSetLineWidth(context, self.drawingThickness)
    CGContextSetStrokeColorWithColor(context, drawingColor)
    CGContextSetBlendMode(context, CGBlendMode.Normal)
    CGContextSetAlpha(context, self.drawingAlpha)
    CGContextStrokePath(context);
}

private func eraseMode()
{
    if (self.image != nil)
    {
        self.image!.drawInRect(self.bounds)
    }
    let context:CGContextRef = UIGraphicsGetCurrentContext()!
    CGContextSaveGState(context)
    CGContextAddPath(context, path);
    CGContextSetLineCap(context, CGLineCap.Round)
    CGContextSetLineWidth(context, self.drawingThickness)
    CGContextSetBlendMode(context, CGBlendMode.Clear)
    CGContextStrokePath(context)
    CGContextRestoreGState(context)
}



private func midPoint (p1:CGPoint, p2:CGPoint)->CGPoint
{
    return CGPointMake((p1.x + p2.x) * 0.5, (p1.y + p2.y) * 0.5)
}

private func finishDrawing()
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0);
    drawViewHierarchyInRect(self.bounds, afterScreenUpdates: true)
    self.image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
}

func clearSignature()
{
    path = CGPathCreateMutable()
    self.image = nil;
    self.setNeedsDisplay();
}

// MARK: - Touch Delegates
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
    path = CGPathCreateMutable()
    let touch = touches.first!
    previousPoint1 = touch.previousLocationInView(self)
    currentPoint = touch.locationInView(self)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    let touch = touches.first!
    previousPoint2 = previousPoint1
    previousPoint1 = touch.previousLocationInView(self)
    currentPoint = touch.locationInView(self)
    
    let mid1 = midPoint(previousPoint2!, p2: previousPoint1!)
    let mid2 = midPoint(currentPoint!, p2: previousPoint1!)
    
    let subpath:CGMutablePathRef = CGPathCreateMutable()
    CGPathMoveToPoint(subpath, nil, mid1.x, mid1.y)
    CGPathAddQuadCurveToPoint(subpath, nil, previousPoint1!.x, previousPoint1!.y, mid2.x, mid2.y)
    CGPathAddPath(path, nil, subpath);
    self.setNeedsDisplay()
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
    self.touchesMoved(touches, withEvent: event)
    self.finishDrawing()
}
override func touchesCancelled(touches: Set<UITouch>?, withEvent event: UIEvent?) {
    self.touchesMoved(touches!, withEvent: event)
    self.finishDrawing()
}

}

Source Code for test app I created using the above code

编辑:根据要求将几行代码转换为swift 3

subpath.move(to: CGPoint(x: mid1.x, y: mid1.y))
subpath.addQuadCurve(to:CGPoint(x: mid2.x, y: mid2.y) , control: CGPoint(x: previousPoint1!.x, y: previousPoint1!.y))
path.addPath(subpath)

编辑:回复更新的问题

这里是更新的绘图类,必须解决这个问题的肯定。https://drive.google.com/file/d/0B5nqEBSJjCriTU5oRXd5c2hRV28/view?usp=sharing&resourcekey=0-8ZE92CSD3j7xxB5jGvgj2w
处理的问题:

  1. Line结构未保存关联的工具类型。每当调用setNeedsDislpay()时,您都会重绘pathArray中的所有对象,并且所有对象都将使用当前选定的工具重绘。我添加了一个新变量associatedTool来解决这个问题。
    1.使用函数beginTransparencyLayer将混合模式设置为kCGBlendModeNormal。由于这在与工具类型相关的所有情况下都很常见,因此导致模式被设置为正常。我把这两行去掉了
    www.example. com (辅助信息:无)
    www.example. com ()
fdbelqdn

fdbelqdn2#

试试这个,它没有错误,而擦除,它可以是我们为绘图,擦除和清除您的屏幕。你甚至可以增加或减少你的铅笔和橡皮擦的大小。U也可以相应地改变颜色。
希望这对你有帮助。....
导入UIKit

class DrawingView: UIView {

var lineColor:CGColor = UIColor.black.cgColor 
var lineWidth:CGFloat = 5
var drawingAlpha:CGFloat = 1.0

var isEraserSelected: Bool

private var currentPoint:CGPoint?
private var previousPoint1:CGPoint?
private var previousPoint2:CGPoint?

private var path:CGMutablePath = CGMutablePath()

var image:UIImage?

required init?(coder aDecoder: NSCoder) {
    //self.backgroundColor = UIColor.clearColor()
    self.isEraserSelected = false
    super.init(coder: aDecoder)
    self.backgroundColor = UIColor.clear
}

override func draw(_ rect: CGRect)
{
    self.isEraserSelected ? self.eraseMode() : self.drawingMode()
}

private func drawingMode()
{
    if (self.image != nil)
    {
        self.image!.draw(in: self.bounds)
    }
    let context:CGContext = UIGraphicsGetCurrentContext()!
    context.addPath(path)
    context.setLineCap(CGLineCap.round)
    context.setLineWidth(self.lineWidth)
    context.setStrokeColor(lineColor)
    context.setBlendMode(CGBlendMode.normal)
    context.setAlpha(self.drawingAlpha)
    context.strokePath();
}

private func eraseMode()
{
    if (self.image != nil)
    {
        self.image!.draw(in: self.bounds)
    }
    let context:CGContext = UIGraphicsGetCurrentContext()!
    context.saveGState()
    context.addPath(path);
    context.setLineCap(CGLineCap.round)
    context.setLineWidth(self.lineWidth)
    context.setBlendMode(CGBlendMode.clear)
    context.strokePath()
    context.restoreGState()
}

private func midPoint (p1:CGPoint, p2:CGPoint)->CGPoint
{
    return CGPoint(x: (p1.x + p2.x) * 0.5, y: (p1.y + p2.y) * 0.5);
}

private func finishDrawing()
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, 0.0);
    drawHierarchy(in: self.bounds, afterScreenUpdates: true)
    self.image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
}

func clearSignature()
{
    path = CGMutablePath()
    self.image = nil;
    self.setNeedsDisplay();
}

// MARK: - Touch Delegates
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    path = CGMutablePath()
    let touch = touches.first!
    previousPoint1 = touch.previousLocation(in: self)
    currentPoint = touch.location(in: self)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touch = touches.first!
    previousPoint2 = previousPoint1
    previousPoint1 = touch.previousLocation(in: self)
    currentPoint = touch.location(in: self)

    let mid1 = midPoint(p1: previousPoint2!, p2: previousPoint1!)
    let mid2 = midPoint(p1: currentPoint!, p2: previousPoint1!)

    let subpath:CGMutablePath = CGMutablePath()
    subpath.move(to: CGPoint(x: mid1.x, y: mid1.y), transform: .identity)
    subpath.addQuadCurve(to: CGPoint(x: mid2.x, y: mid2.y), control: CGPoint(x: (previousPoint1?.x)!, y: (previousPoint1?.y)!))
    path.addPath(subpath, transform: .identity)

    self.setNeedsDisplay()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    self.touchesMoved(touches, with: event)
    self.finishDrawing()
}
override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
    self.touchesMoved(touches!, with: event)
    self.finishDrawing()
}
}

相关问题