Swift中无需Timer和下拉刷新,数组变化时自动更新TableView单元格
嘿,这个问题我熟!在UIKit里不用Timer或者下拉刷新,照样能让表格跟着loadedCart数组的变化自动更新,给你分享几个实用的方案,你按需选:
方案1:Combine + ObservableObject(推荐,现代Swift写法)
这个方案把数据管理集中到一个共享的ViewModel里,利用Combine的发布订阅机制,数组一变就自动通知表格刷新,代码清晰还解耦。
首先创建一个全局共享的购物车ViewModel:
import Combine class CartViewModel: ObservableObject { // 单例模式,所有ViewController共享同一个实例 static let shared = CartViewModel() // @Published标记的属性,变化时会自动发送通知 @Published var loadedCart: [[String: Any]] = [] private init() { // 从UserDefaults加载初始数据,和你原来的逻辑保持一致 let def = UserDefaults.standard loadedCart = def.array(forKey: "cartKey") as? [[String: Any]] ?? [] } // 封装添加/删除方法,统一管理数据修改 func addCartItem(_ item: [String: Any]) { loadedCart.append(item) saveToUserDefaults() } func removeCartItem(at index: Int) { guard index < loadedCart.count else { return } loadedCart.remove(at: index) saveToUserDefaults() } // 私有方法,保存数据到UserDefaults private func saveToUserDefaults() { let def = UserDefaults.standard def.set(loadedCart, forKey: "cartKey") } }
然后修改你的TestTABLEVC,订阅ViewModel的数组变化:
import Combine import UIKit class TestTABLEVC: UIViewController, UITableViewDataSource, UITableViewDelegate { @IBOutlet weak var tableViewT: UITableView! // 用来保存Combine订阅,防止被提前释放 private var cancellables = Set<AnyCancellable>() // 获取共享的购物车ViewModel private let cartVM = CartViewModel.shared override func viewDidLoad() { super.viewDidLoad() tableViewT.dataSource = self tableViewT.delegate = self // 记得注册你的自定义Cell(如果之前没做的话) tableViewT.register(TestTableViewCell.self, forCellReuseIdentifier: "TestCell") // 订阅loadedCart的变化,一旦数组更新就刷新表格 cartVM.$loadedCart .receive(on: DispatchQueue.main) // 确保在主线程更新UI .sink { [weak self] _ in self?.tableViewT.reloadData() } .store(in: &cancellables) } // 实现TableView数据源方法,直接用ViewModel里的数组count func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return cartVM.loadedCart.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as! TestTableViewCell // 这里根据数组内容配置Cell,比如显示商品信息或者计数 cell.textLabel?.text = "商品 \(indexPath.row + 1)" return cell } // 其他Delegate方法按需实现... }
之后在其他ViewController里修改购物车时,直接调用ViewModel的方法就行,不用管通知的事:
// 添加商品示例 let newItem: [String: Any] = ["name": "橙子", "price": 6.99] CartViewModel.shared.addCartItem(newItem) // 删除商品示例(删除第一个) CartViewModel.shared.removeCartItem(at: 0)
方案2:NotificationCenter(传统UIKit写法)
如果不想用Combine,用系统自带的通知中心也能搞定,逻辑是数组变化时发送通知,表格VC监听通知后刷新。
首先定义一个专属的通知名称:
extension Notification.Name { static let cartUpdated = Notification.Name("cartUpdated") }
然后在任何修改loadedCart的地方,修改完成后发送通知:
// 比如添加商品后 loadedCart.append(newItem) def.set(loadedCart, forKey: "cartKey") // 发送通知告诉表格要更新了 NotificationCenter.default.post(name: .cartUpdated, object: nil) // 删除商品后同理 loadedCart.remove(at: index) def.set(loadedCart, forKey: "cartKey") NotificationCenter.default.post(name: .cartUpdated, object: nil)
最后在TestTABLEVC里监听这个通知:
class TestTABLEVC: UIViewController, UITableViewDataSource, UITableViewDelegate { @IBOutlet weak var tableViewT: UITableView! var def = UserDefaults.standard var loadedCart = [[String:Any]]() override func viewDidLoad() { super.viewDidLoad() tableViewT.dataSource = self tableViewT.delegate = self // 加载初始数据 loadedCart = def.array(forKey: "cartKey") as? [[String: Any]] ?? [] // 监听购物车更新通知 NotificationCenter.default.addObserver( self, selector: #selector(refreshCartTable), name: .cartUpdated, object: nil ) } // 收到通知后刷新表格 @objc private func refreshCartTable() { // 重新加载最新数据 loadedCart = def.array(forKey: "cartKey") as? [[String: Any]] ?? [] tableViewT.reloadData() } // 记得在VC销毁时移除观察者,避免内存泄漏 deinit { NotificationCenter.default.removeObserver(self) } // 数据源方法... func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return loadedCart.count } // 其他方法按需实现... }
方案3:利用属性的didSet(简单直接但耦合性稍高)
如果把loadedCart放到一个共享的单例对象里,给它加上didSet监听,数组变化时自动触发刷新逻辑:
class CartManager { static let shared = CartManager() var loadedCart: [[String: Any]] = [] { didSet { // 保存到UserDefaults let def = UserDefaults.standard def.set(loadedCart, forKey: "cartKey") // 发送通知告诉表格更新 NotificationCenter.default.post(name: .cartUpdated, object: nil) } } private init() { let def = UserDefaults.standard loadedCart = def.array(forKey: "cartKey") as? [[String: Any]] ?? [] } }
之后在其他VC里直接修改CartManager.shared.loadedCart,didSet会自动触发并发送通知,TestTABLEVC的监听逻辑和方案2一样就行。
内容的提问来源于stack exchange,提问作者user9696953
相关产品推荐
相关产品推荐

