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

TableView重载后UI未正确更新:缺货状态显示异常求助

问题诊断与修复方案

这种异步请求和TableView数据不同步的问题确实让人头大,我帮你梳理下代码里的几个关键问题,应该就能解决了:

核心问题分析

1. 异步请求的顺序与数据覆盖问题

你在getProductList的循环里每次都清空availableIdArray然后调用getProductAvailability,但网络请求是异步的——这意味着第一个请求还没返回,第二个请求就已经把数组清空了,最后只有最后一个请求的结果会留在数组里,前面的全丢了。而且你每次调用getProductAvailability后立刻发通知,这时候请求可能还没完成,TableView拿到的是不完整的数据。

2. Cell复用与显示逻辑错误

在cellForRowAt里,你遍历整个availableIdArray来判断当前cell的显示状态,这会导致逻辑混乱:比如当前cell的商品ID在数组里存在,遍历到它时你设置isHidden = false,但继续遍历到下一个不匹配的ID时,又会把它设回isHidden = true,最后只有最后一个匹配的cell会显示正确,其他的都会被错误隐藏。

3. 全局数组的管理混乱

切换分类时,你没有确保在新的请求开始前完全清空旧的库存数据,导致新分类的商品和旧分类的库存数据混在一起,显示自然出错。

修复后的代码示例

第一步:重构数据请求逻辑,确保异步请求完成后再更新UI

我们用DispatchGroup来管理所有异步库存请求,等全部完成后再刷新TableView,避免数据混乱:

public func getProductList(request : ProductRequest) {
    User.apiProductList(Request: request) {(response, error) in
        guard let array = response.obj as? [Products], !array.isEmpty else {
            print("No Products…\n")
            // 清空旧数据并刷新
            Home.productListArray = []
            Home.productAvailableArray = []
            Home.availableIdArray = []
            NotificationCenter.default.post(name: .reload, object: nil)
            return
        }
        
        Home.productListArray = array
        // 清空旧的库存数据
        Home.productAvailableArray.removeAll()
        Home.availableIdArray.removeAll()
        
        let dispatchGroup = DispatchGroup()
        
        for product in array {
            dispatchGroup.enter()
            // 复制请求对象,修改为当前商品的ID(这里需要你根据实际请求结构调整)
            var productAvailabilityRequest = availabilityRequest
            productAvailabilityRequest.productId = product.productId
            
            User.apiProductAvailability(Request: productAvailabilityRequest, callback: {(response, error) in
                defer {
                    dispatchGroup.leave()
                }
                guard let object = response.obj as? AvailableProduct else {
                    return
                }
                Home.availableIdArray.append(object.productId! as! Int)
                Home.productAvailableArray.append(object)
            })
        }
        
        // 所有库存请求完成后统一刷新TableView
        dispatchGroup.notify(queue: .main) {
            NotificationCenter.default.post(name: .reload, object: nil)
        }
    }
}

注意:你需要调整availabilityRequest的创建逻辑,让它能接收当前商品的ID,不然每个请求都会查同一个商品的库存,这肯定不对!

第二步:修复Cell的显示逻辑

把遍历数组的逻辑改成判断当前商品ID是否在缺货列表里,同时先设置默认状态避免复用旧UI:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "productcell", for: indexPath) as! ProductViewCell
    let data = Home.productListArray[indexPath.row]
    
    // 先设置默认隐藏,避免复用旧cell的显示状态
    cell.availableProductView.isHidden = true
    expandedRows.remove(indexPath)
    
    // 只需要判断当前商品ID是否在缺货列表中
    if data.productId != 0 && Home.availableIdArray.contains(data.productId) {
        cell.availableProductView.isHidden = false
        expandedRows.insert(indexPath)
    }
    
    // 别忘了设置商品详情的UI数据
    // cell.productDetailView.configure(with: data)
    
    return cell
}

第三步:优化刷新和分类切换的逻辑

在刷新和分类切换时,确保先清空所有旧数据,再发起新请求:

@objc func refreshAction(){
    self.refreshControl.endRefreshing()
    // 清空所有旧数据,避免新旧数据混杂
    Home.productListArray.removeAll()
    Home.productAvailableArray.removeAll()
    Home.availableIdArray.removeAll()
    getProductList(request: productListRequest)
}

第四步:移除不必要的通知发送

删掉getProductAvailability里单独发送的刷新通知,因为我们已经用DispatchGroup在所有请求完成后统一刷新了,单独发送会导致TableView在数据不完整时更新。

额外建议

  • 尽量不要用全局数组管理TableView数据,最好把数据封装在当前ViewController里,或者用ViewModel来管理,全局变量很容易导致数据混乱。
  • 给模型添加可选值安全处理,避免强制解包(比如object.productId! as! Int),减少崩溃风险。

内容的提问来源于stack exchange,提问作者Bella

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 07:18:40