Swift开发:如何保存选中图片并传递至TableView所在的主ViewController
Hey there! Welcome to Stack Overflow—super glad you’re finding the platform helpful for your dev questions, and awesome to hear your Swift project is moving along smoothly. Let’s figure out how to get that selected image from your second view controller over to the main one, plus cover saving if you need it.
First off, using a constant.swift file for data transfer works for simple text, but it’s not ideal for images (they’re larger objects and global storage can lead to memory bloat or unexpected state). Let’s go with cleaner, more scalable approaches:
This is my go-to for simple reverse data flow because it’s compact and doesn’t require extra protocol setup.
Step 1: Set up the closure in your second ViewController
Add a closure property to capture the submitted title and image, then trigger it when the user taps "done":
class SecondViewController: UIViewController { @IBOutlet weak var titleTextField: UITextField! @IBOutlet weak var selectedImageView: UIImageView! // Assume you have this to preview the image private var pickedImage: UIImage? // Define the closure to pass data back var onSubmit: ((String, UIImage?) -> Void)? // Handle image picker selection (you likely already have this) func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { pickedImage = info[.originalImage] as? UIImage selectedImageView.image = pickedImage picker.dismiss(animated: true) } // Triggered when user taps "done" to submit @IBAction func doneButtonTapped(_ sender: UIButton) { guard let title = titleTextField.text, !title.trimmingCharacters(in: .whitespaces).isEmpty else { // Show an alert if title is empty (optional but user-friendly) let alert = UIAlertController(title: "Oops", message: "Please enter a title", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default)) present(alert, animated: true) return } // Pass data back via closure onSubmit?(title, pickedImage) dismiss(animated: true) } }
Step 2: Hook up the closure in your main ViewController
When you present the second VC, assign the closure to handle the incoming data and refresh your table view:
class MainViewController: UIViewController, UITableViewDataSource { @IBOutlet weak var tableView: UITableView! // Store your items (title + image) here private var items: [(title: String, image: UIImage?)] = [] @IBAction func addButtonTapped(_ sender: UIButton) { guard let secondVC = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return } // Set up the closure to receive data secondVC.onSubmit = { [weak self] title, image in guard let self = self else { return } // Add the new item to your array self.items.append((title: title, image: image)) // Refresh the table view to show the new item self.tableView.reloadData() } present(secondVC, animated: true) } // MARK: - UITableView DataSource func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ItemCell", for: indexPath) as! ItemCell // Use your custom cell class let item = items[indexPath.row] cell.titleLabel.text = item.title cell.itemImageView.image = item.image return cell } }
If you’re working on a larger project where you might need more complex communication between VCs, delegates are a classic, scalable choice.
Step 1: Define a delegate protocol
Add this outside your second VC class:
protocol SecondViewControllerDelegate: AnyObject { func didSubmitItem(title: String, image: UIImage?) }
Step 2: Add a delegate property to your second VC
class SecondViewController: UIViewController { weak var delegate: SecondViewControllerDelegate? // ... (keep your existing image picker and title text field code) @IBAction func doneButtonTapped(_ sender: UIButton) { guard let title = titleTextField.text, !title.trimmingCharacters(in: .whitespaces).isEmpty else { // Show alert as before return } // Call the delegate method to pass data back delegate?.didSubmitItem(title: title, image: pickedImage) dismiss(animated: true) } }
Step 3: Conform to the delegate in your main VC
class MainViewController: UIViewController, UITableViewDataSource, SecondViewControllerDelegate { // ... (keep your items array and table view code) @IBAction func addButtonTapped(_ sender: UIButton) { guard let secondVC = storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return } secondVC.delegate = self present(secondVC, animated: true) } // Implement the delegate method func didSubmitItem(title: String, image: UIImage?) { items.append((title: title, image: image)) tableView.reloadData() } }
If you want to keep images even after the app closes, save them to the app’s document directory instead of just keeping them in memory:
Save the image to disk
func saveImageToDisk(_ image: UIImage) -> URL? { guard let imageData = image.pngData() else { return nil } // Use jpegData(compressionQuality: 0.8) for smaller files let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first! let imageFileName = UUID().uuidString + ".png" // Unique name to avoid overwrites let imageURL = documentsDir.appendingPathComponent(imageFileName) do { try imageData.write(to: imageURL) return imageURL } catch { print("Failed to save image: \(error.localizedDescription)") return nil } }
Load the image from disk later
func loadImageFromDisk(url: URL) -> UIImage? { do { let imageData = try Data(contentsOf: url) return UIImage(data: imageData) } catch { print("Failed to load image: \(error.localizedDescription)") return nil } }
Instead of passing the UIImage directly, you’d pass the saved URL, then load it when displaying in the table view cell. This is better for memory if you have lots of images.
内容的提问来源于stack exchange,提问作者Nick Eßer

