如何通过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 } } }
关键修改点说明
- 数据绑定:合并模型后,每个条目对应的文本和图片是绑定的,不会出现图片和文本不匹配的问题。
- 数组存储:用数组代替单个变量,每次新增条目都会追加到数组里,旧数据不会被覆盖。
- 安全处理:添加文本框非空判断,避免强制解包导致的崩溃;图片设为可选,允许用户不选择图片。
- 动画效果:用
insertRows配合beginUpdates和endUpdates实现平滑的新增行动画,提升用户体验。
内容的提问来源于stack exchange,提问作者alias.marv
相关产品推荐
相关产品推荐

