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

如何通过ViewController间传结构体为UITableView添加新行?

解决UITableView新增条目覆盖旧数据的问题

问题根源:你当前在VC1里用单个item和itemImg变量存储数据,每次从VC2传新数据过来都会覆盖旧值,所以TableView始终只能显示一行。要实现新增行,需要改用数组存储所有条目,并且把文本和图片绑定成一个统一的模型,避免分开传递导致的数据不匹配。

步骤1:重构数据模型

把原来分开的两个结构体合并成一个Book模型,确保每个条目包含完整的文本和图片信息:

// 在VC2的代码里替换原来的两个结构体
struct Book {
    let title: String
    let author: String
    let currentPage: String
    let totalPages: String
    let image: UIImage? // 图片设为可选,允许用户不选择图片
}

步骤2:修改协议

简化协议,只需要传递完整的Book对象,避免分开传文本和图片:

// 替换原来的SendingBookDataProtocol
protocol SendingBookDataProtocol {
    func sendBook(_ book: Book)
}

步骤3:修改VC2代码

  • 新增变量存储选中的图片,等保存时和文本一起传递
  • 增加文本框非空判断,避免强制解包崩溃
  • 点击保存按钮时封装完整的Book对象传给VC1

修改后的VC2代码:

import UIKit

protocol SendingBookDataProtocol {
    func sendBook(_ book: Book)
}

struct Book {
    let title: String
    let author: String
    let currentPage: String
    let totalPages: String
    let image: UIImage?
}

class AddBookController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
    var delegate: SendingBookDataProtocol? = nil
    private var selectedImage: UIImage? // 存储选中的图片
    
    // 假设你已经关联了这些IBOutlet
    @IBOutlet weak var textfieldTitle: UITextField!
    @IBOutlet weak var textfieldAuthor: UITextField!
    @IBOutlet weak var fieldCurrentPage: UITextField!
    @IBOutlet weak var fieldTotalPages: UITextField!
    @IBOutlet weak var imageViewAddBook: UIImageView!

    @IBAction func buttonSave(_ sender: Any) {
        // 先检查所有必填字段是否为空
        guard let title = textfieldTitle.text, !title.isEmpty,
              let author = textfieldAuthor.text, !author.isEmpty,
              let currentPage = fieldCurrentPage.text, !currentPage.isEmpty,
              let totalPages = fieldTotalPages.text, !totalPages.isEmpty else {
            // 提示用户填写完整信息
            let alert = UIAlertController(title: "提示", message: "请填写所有必填信息", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "确定", style: .default))
            present(alert, animated: true)
            return
        }
        
        // 封装完整的Book对象
        let book = Book(title: title, author: author, currentPage: currentPage, totalPages: totalPages, image: selectedImage)
        delegate?.sendBook(book)
        dismiss(animated: true)
    }

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        // 优先取编辑后的图片,没有则取原图
        let pickedImage: UIImage?
        if let editedImage = info[.editedImage] as? UIImage {
            pickedImage = editedImage
        } else {
            pickedImage = info[.originalImage] as? UIImage
        }
        
        if let image = pickedImage {
            selectedImage = image
            imageViewAddBook.image = image
        }
        
        dismiss(animated: true)
    }
}

步骤4:修改VC1代码

  • 用数组存储所有Book条目
  • 更新TableView的数据源方法,基于数组数量返回行数
  • 新增条目时用insertRows实现平滑动画

修改后的VC1代码:

import UIKit

class HomeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SendingBookDataProtocol {

    private var books: [Book] = [] // 数组存储所有书籍
    @IBOutlet weak var tableView: UITableView! // 确保关联了TableView的IBOutlet

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.delegate = self
        tableView.dataSource = self
        // 如果storyboard里没注册cell,需要手动注册
        // tableView.register(BookCell.self, forCellReuseIdentifier: "BookCell")
    }

    // 接收VC2传来的Book对象
    func sendBook(_ book: Book) {
        books.append(book)
        // 插入新行并添加动画
        let newIndexPath = IndexPath(row: books.count - 1, section: 0)
        tableView.beginUpdates()
        tableView.insertRows(at: [newIndexPath], with: .automatic)
        tableView.endUpdates()
        // 也可以直接用tableView.reloadData(),但insertRows有动画更友好
    }

    // 返回数组的元素数量,也就是TableView的行数
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return books.count
    }

    // 为每个cell设置对应的数据
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "BookCell", for: indexPath) as! BookCell
        let currentBook = books[indexPath.row]
        
        cell.title.text = currentBook.title
        cell.author.text = currentBook.author
        cell.currentPage.text = currentBook.currentPage
        cell.totalPages.text = currentBook.totalPages
        // 没有图片时可以用占位图替换
        cell.bookImage.image = currentBook.image ?? UIImage(named: "default-book-cover")
        
        return cell
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "getBookData" {
            let addBookVC = segue.destination as! AddBookController
            addBookVC.delegate = self
        }
    }
}

关键修改点说明

  1. 数据绑定:合并模型后,每个条目对应的文本和图片是绑定的,不会出现图片和文本不匹配的问题。
  2. 数组存储:用数组代替单个变量,每次新增条目都会追加到数组里,旧数据不会被覆盖。
  3. 安全处理:添加文本框非空判断,避免强制解包导致的崩溃;图片设为可选,允许用户不选择图片。
  4. 动画效果:用insertRows配合beginUpdates和endUpdates实现平滑的新增行动画,提升用户体验。

内容的提问来源于stack exchange,提问作者alias.marv

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 22:01:16