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
backgroundImageofDrawView, which gets rendered first in thedraw(_:)method before your doodles. - The
getFinalDrawnImage()method captures the entireDrawView(background image + all strokes) as a singleUIImage. - The save action uses
UIImageWriteToSavedPhotosAlbumto store the final image in the user's photo library, with feedback alerts for success/failure.
内容的提问来源于stack exchange,提问作者Agung

