在 Swift 程序中,为什么我的橡皮擦工具出现像素化?

问题描述 投票:0回答:2

关于在 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()
            }
            
        }
    }

我仍然得到相同的结果。接下来我可以尝试什么?

ios swift3 core-graphics eraser
2个回答
1
投票

我无法准确查看您的代码。但不久前我在 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()
}

}

我使用上面的代码创建的测试应用程序的源代码

编辑:根据要求将几行代码转换为 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
    来解决这个问题。
  2. 使用功能
    beginTransparencyLayer
    会将混合模式设置为
    kCGBlendModeNormal
    。由于这对于与工具类型相关的所有情况都很常见,因此导致模式设置为正常。我已经删除了这两行

//context.beginTransparencyLayer(auxiliaryInfo: nil)

//context.endTransparencyLayer()


1
投票

尝试一下,擦除时不会出现错误,可以用于绘图擦除和清除屏幕。您甚至可以增加或减少铅笔和橡皮的尺寸。你也可以相应地改变颜色。

希望这对你有帮助......

导入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()
}
}
© www.soinside.com 2019 - 2024. All rights reserved.