You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift实现图片自由涂鸦并保存为UIImage的技术求助

Hey there! Let's tweak your existing code to support drawing on a selected image (from camera/photo library) and saving the final result as a new UIImage. Here's a step-by-step breakdown:

Step 1: Update DrawView to support a background image

First, we'll add a property to hold the background image, and adjust the draw(_:) method to render this image before drawing the strokes. We'll also add a method to export the final combined image.

Modify your DrawView class like this:

class DrawView : ShadowView {
    var isDrawing = false
    var lastPoint : CGPoint!
    var strokeColor : CGColor! = UIColor.black.cgColor
    var strokes = [Stroke]()
    // Add property to hold the background image
    var backgroundImage: UIImage?
    
    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
    }
    
    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()
    }
    
    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
    }
    
    override func draw(_ rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()
        // First render the background image if it exists
        if let bgImage = backgroundImage {
            bgImage.draw(in: rect)
        }
        // Then draw all the saved strokes
        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()
    }
    
    // Method to capture the final image (background + strokes)
    func getFinalDrawnImage() -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, UIScreen.main.scale)
        drawHierarchy(in: bounds, afterScreenUpdates: true)
        let finalImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return finalImage
    }
}

The Stroke struct stays unchanged:

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

Step 2: Update ViewController for image selection and save functionality

Next, we'll add code to let users pick an image from their photo library or camera, set it as the background of DrawView, and add a save button to store the final result.

Update your ViewController to conform to image picker delegates:

import UIKit

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    @IBOutlet weak var drawView: DrawView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    // MARK: - Color Selection Actions
    @IBAction func yellowDidPressed(_ sender: Any) {
        drawView.strokeColor = UIColor.yellow.cgColor
    }
    
    @IBAction func redDidPressed(_ sender: Any) {
        drawView.strokeColor = UIColor.red.cgColor
    }
    
    @IBAction func blueDidPressed(_ sender: Any) {
        drawView.strokeColor = UIColor.blue.cgColor
    }
    
    @IBAction func erasedDidPressed(_ sender: Any) {
        drawView.erase()
    }
    
    // MARK: - Image Selection Actions
    @IBAction func pickFromPhotoLibrary(_ sender: Any) {
        let picker = UIImagePickerController()
        picker.sourceType = .photoLibrary
        picker.delegate = self
        present(picker, animated: true)
    }
    
    @IBAction func takePhoto(_ sender: Any) {
        guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
            let alert = UIAlertController(title: "Camera Unavailable", message: "Your device doesn't support camera access.", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default))
            present(alert, animated: true)
            return
        }
        let picker = UIImagePickerController()
        picker.sourceType = .camera
        picker.delegate = self
        present(picker, animated: true)
    }
    
    // MARK: - Image Picker Delegate
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        dismiss(animated: true)
        guard let selectedImage = info[.originalImage] as? UIImage else { return }
        // Set the selected image as background and clear existing strokes
        drawView.backgroundImage = selectedImage
        drawView.erase()
    }
    
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        dismiss(animated: true)
    }
    
    // MARK: - Save Final Image
    @IBAction func saveImage(_ sender: Any) {
        guard let finalImage = drawView.getFinalDrawnImage() else { return }
        // Save to photo library
        UIImageWriteToSavedPhotosAlbum(finalImage, self, #selector(imageSaveCompleted(_:didFinishSavingWithError:contextInfo:)), nil)
    }
    
    @objc func imageSaveCompleted(_ image: UIImage, didFinishSavingWithError error: Error?, contextInfo: UnsafeRawPointer) {
        let alert: UIAlertController
        if let error = error {
            alert = UIAlertController(title: "Save Failed", message: error.localizedDescription, preferredStyle: .alert)
        } else {
            alert = UIAlertController(title: "Save Successful", message: "Your image is saved to your photo library!", preferredStyle: .alert)
        }
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        present(alert, animated: true)
    }
}

Step 3: Add missing UI elements

Don't forget to add these buttons to your storyboard/XIB and connect their actions:

  • Button to pick image from library (connect to pickFromPhotoLibrary(_:))
  • Button to take a photo (connect to takePhoto(_:))
  • Save button (connect to saveImage(_:))

How it works:

  • When you select an image, it's set as the backgroundImage of DrawView, which gets rendered first in the draw(_:) method before your doodles.
  • The getFinalDrawnImage() method captures the entire DrawView (background image + all strokes) as a single UIImage.
  • The save action uses UIImageWriteToSavedPhotosAlbum to store the final image in the user's photo library, with feedback alerts for success/failure.

内容的提问来源于stack exchange,提问作者Agung

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:43:56