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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:28:38