【问题标题】:how to draw / doodle line on UIImage in swift?如何快速在 UIImage 上绘制/涂鸦线?
【发布时间】:2019-05-08 08:50:35
【问题描述】:

我需要像上图那样在 UIImage 中画/涂鸦一条线,我看到很多教程在 UIView 上涂鸦线,但在 UIImage 中没有。

用户在图像上涂鸦后,我想将其保存为新图像(有线条的图像)。我如何在 Swift 中做到这一点?

我只能在 UIView 上找到画线,而不是在 UIImage 中 UIView 上的绘图就像这个 youtube 教程 http://youtube.com/watch?v=gbFHqHHApC4 中的这个,我将其更改为继承 DrawView 到 UIImageView

   struct  Stroke {
    let startPoint : CGPoint
    let endPoint : CGPoint
    let strokeColor : CGColor
}


class DrawView : UIImageView {

    var isDrawing = false
    var lastPoint : CGPoint!
    var strokeColor : CGColor! = UIColor.black.cgColor
    var strokes = [Stroke]()


    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

        guard !isDrawing else { return }
        isDrawing = true

        guard let touch = touches.first else {return}
        let currentPoint = touch.location(in: self)
        lastPoint = currentPoint
        print(currentPoint)


    }



    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

        guard isDrawing else { return}

        guard let touch = touches.first else {return}
        let currentPoint = touch.location(in: self)
        let stroke = Stroke(startPoint: lastPoint, endPoint: currentPoint, strokeColor: strokeColor)
        strokes.append(stroke)
        lastPoint = currentPoint

        setNeedsDisplay()


        print(currentPoint)
    }




    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {

        guard isDrawing else { return}
        isDrawing = false


        guard let touch = touches.first else {return}

        let currentPoint = touch.location(in: self)
        let stroke = Stroke(startPoint: lastPoint, endPoint: currentPoint, strokeColor: strokeColor)
        strokes.append(stroke)

        setNeedsDisplay()

        lastPoint = nil
        print(currentPoint)

    }


    override func draw(_ rect: CGRect) {

        let context = UIGraphicsGetCurrentContext()
        context?.setLineWidth(5)
        context?.setLineCap(.round)

        for stroke in strokes {
            context?.beginPath()

            context?.move(to: stroke.startPoint)
            context?.addLine(to: stroke.endPoint)

            context?.setStrokeColor(stroke.strokeColor)
            context?.strokePath()
        }

    }


    func erase() {
        strokes = []
        strokeColor = UIColor.black.cgColor
        setNeedsDisplay()
    }






}

我已将情节提要中的 UIImage 指定为使用自定义类“DrawView”,就像我上面的代码一样,但我不知道为什么这些线条没有出现在我的 UIImage 上

【问题讨论】:

  • 尝试使用 2 张图片。
  • 尝试使用 2 个图像 1. 包含当前绘制的临时图像 2. 包含其他已创建图像的主图像
  • 你有没有尝试过?如果是,请显示代码以及什么不起作用。
  • imageView 上画线并从中捕获新图像?
  • 是的,我需要类似@TheTiger 的东西

标签: ios swift uiimage


【解决方案1】:

详情

  • Xcode 10.2.1 (10E1001)、Swift 5

解决方案

import UIKit

// https://stackoverflow.com/a/41009006/4488252
class DrawnImageView: UIImageView {
    private lazy var path = UIBezierPath()
    private lazy var previousTouchPoint = CGPoint.zero
    private lazy var shapeLayer = CAShapeLayer()

    override func awakeFromNib() {
        super.awakeFromNib()
        setupView()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    func setupView(){
        layer.addSublayer(shapeLayer)
        shapeLayer.lineWidth = 4
        shapeLayer.strokeColor = UIColor.white.cgColor
        isUserInteractionEnabled = true
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesBegan(touches, with: event)
        if let location = touches.first?.location(in: self) { previousTouchPoint = location }
    }

    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
        super.touchesMoved(touches, with: event)
        if let location = touches.first?.location(in: self) {
            path.move(to: location)
            path.addLine(to: previousTouchPoint)
            previousTouchPoint = location
            shapeLayer.path = path.cgPath
        }
    }
}

// https://stackoverflow.com/a/40953026/4488252
extension UIView {
    var screenShot: UIImage?  {
        let scale = UIScreen.main.scale
        UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale)
        if let context = UIGraphicsGetCurrentContext() {
            layer.render(in: context)
            let screenshot = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return screenshot
        }
        return nil
    }
}

用法

  1. 将 DrawnImageView 添加到您的根(父)视图(触摸绘图将自动启用)
  2. 要保存 UIImage 使用 drawingImageView.screenShot

完整样本

不要忘记在此处添加解决方案代码

import UIKit

class ViewController: UIViewController {

    fileprivate weak var savedImageView: UIImageView?
    fileprivate weak var drawnImageView: UIImageView?

    override func viewDidLoad() {
        super.viewDidLoad()

        let drawnImageView = addImageView(image: UIImage(named: "swift")) as DrawnImageView
        drawnImageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        drawnImageView.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height/3).isActive = true
        self.drawnImageView = drawnImageView

        let button = UIButton()
        button.setTitle("Save Image", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitleColor(.blue, for: .normal)
        view.addSubview(button)
        button.topAnchor.constraint(equalTo: drawnImageView.bottomAnchor, constant: 60).isActive = true
        button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        button.heightAnchor.constraint(equalToConstant: 44).isActive = true
        button.addTarget(self, action: #selector(saveImageButtonTouchUpInside), for: .touchUpInside)

        let savedImageView = addImageView()
        savedImageView.topAnchor.constraint(equalTo: button.bottomAnchor, constant: 60).isActive = true
        savedImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        self.savedImageView = savedImageView
    }

    private func addImageView<T: UIImageView>(image: UIImage? = nil) -> T {
        let imageView = T(frame: .zero)
        imageView.contentMode = .scaleAspectFit
        imageView.image = image
        view.addSubview(imageView)

        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        imageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        return imageView
    }

    @objc func saveImageButtonTouchUpInside(sender: UIButton) {
        savedImageView?.image = drawnImageView?.screenShot
    }
}

结果

【讨论】:

  • 很好的答案!但是我在相册中使用UIImageWriteToSavedPhotosAlbum保存图片,图片有白色边框如何修复?
  • @Beginnerrrrrrr 很难理解发生了什么。我建议您提出新问题,您将在哪里共享模拟问题的代码。因为,我不明白你的问题可能出在哪里。
  • 很棒的代码,但它不支持改变颜色。就像在给定的示例中一样,如果我想分别用红色、绿色、黄色、蓝色和白色来表示“S”、“W”、“I”、“F”和“T”,那么我无法处理你的代码。
  • @MrugeshTank 你可以改变颜色shapeLayer.strokeColor = UIColor.white.cgColor。所以,围绕这条线来创建你需要的功能
  • 已经试过了,但是如果我设置红色并绘制“S”,然后如果我设置绿色绘制“W”,那么“S”也会将其颜色更改为绿色
【解决方案2】:

您可以将UIImageView 放在背景中,将UIView 放在其顶部,然后将UIView 捕获为图像并合并它。

参考这个来捕获 UIView:How to capture UIView to UIImage without loss of quality on retina display

然后使用合并它:

func mergeImages (forgroundImage : UIImage, backgroundImage : UIImage, size : CGSize) -> UIImage {

     let bottomImage = backgroundImage
     UIGraphicsBeginImageContext(size)

     let areaSize = CGRect(x: 0, y: 0, width: size.width, height: size.height)
     bottomImage.draw(in: areaSize)

     let topImage      = forgroundImage
     topImage.draw(in: areaSize, blendMode: .normal, alpha: 1.0)

     let newImage:UIImage = UIGraphicsGetImageFromCurrentImageContext()!

     UIGraphicsEndImageContext()

   return newImage
}

【讨论】:

    【解决方案3】:

    这可能对您有所帮助。参考链接

    How do I draw on an image in Swift?

    1. 创建图像图形上下文。 (在 iOS 10 之前,您可以通过调用 UIGraphicsBeginImageContextWithOptions 来完成此操作。在 iOS 10 中还有另一种方法,即 UIGraphicsImageRenderer,但如果您不想使用,则不必使用它。)

    2. 将图像绘制(即复制)到上下文中。 (UIImage 实际上有 draw... 用于此目的的方法。)

    3. 在上下文中画线。 (为此有 CGContext 函数。)

    4. 从上下文中提取生成的图像。 (例如,如果您使用 UIGraphicsBeginImageContextWithOptions,您将使用 UIGraphicsGetImageFromCurrentImageContext。)然后关闭上下文。

    【讨论】:

      猜你喜欢
      • 2014-07-30
      • 1970-01-01
      • 2020-03-13
      • 2012-02-27
      • 1970-01-01
      • 1970-01-01
      • 2016-05-20
      • 2014-03-20
      • 1970-01-01
      相关资源
      最近更新 更多