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

UICollectionView Cell加载SKProduct价格时跳变问题求助

解决UICollectionView Cell内购价格跳变问题

完全同意你的想法——必须把SKProduct的获取和价格格式化逻辑移到TopUpViewController中,而不是在Cell里处理。你遇到的价格跳变问题,本质是UICollectionView的Cell复用机制和异步价格请求的冲突,下面一步步给你讲清楚怎么改:

为什么之前会跳变?

当你在Cell里直接请求SKProduct价格时,Cell被复用(比如滑动列表)后,之前Cell发起的异步请求可能还没完成。等请求返回时,这个Cell已经被用来展示另一个产品了,旧的价格就会覆盖新Cell的内容,导致跳变。把逻辑移到VC里预加载所有数据,就能从根源解决这个问题。

具体实现步骤

1. 在TopUpViewController中预加载所有产品的本地化价格

首先,你需要提前获取所有4个内购产品,并把每个产品的本地化价格格式化好,存起来。建议封装一个简单的模型来统一管理产品和价格:

// 自定义模型,统一管理内购产品和格式化后的价格
struct TopUpProductModel {
    let product: SKProduct
    let localizedPrice: String
}

// 在TopUpViewController里定义存储数组
private var topUpProducts: [TopUpProductModel] = []

然后实现SKProductsRequest的请求逻辑,在VC的生命周期方法(比如viewDidLoad)里发起请求:

override func viewDidLoad() {
    super.viewDidLoad()
    fetchTopUpProducts()
}

private func fetchTopUpProducts() {
    // 替换成你的4个内购产品ID
    let productIDs = Set(["com.yourapp.product.1", "com.yourapp.product.2", "com.yourapp.product.3", "com.yourapp.product.4"])
    let request = SKProductsRequest(productIdentifiers: productIDs)
    request.delegate = self
    request.start()
}

// 实现SKProductsRequestDelegate协议
extension TopUpViewController: SKProductsRequestDelegate {
    func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) {
        // 格式化价格的工具
        let priceFormatter = NumberFormatter()
        priceFormatter.numberStyle = .currency
        
        // 遍历返回的产品,生成模型数组
        topUpProducts = response.products.map { product in
            priceFormatter.locale = product.priceLocale
            let priceStr = priceFormatter.string(from: product.price) ?? "\(product.price)"
            return TopUpProductModel(product: product, localizedPrice: priceStr)
        }
        
        // 数据准备好后刷新CollectionView
        yourCollectionView.reloadData()
    }
    
    func request(_ request: SKRequest, didFailWithError error: Error) {
        // 处理请求失败的情况,比如提示用户
        print("产品请求失败:\(error.localizedDescription)")
    }
}

2. 在数据源方法中传递数据给TopUpMoneyCell

现在所有价格都预加载好了,只需要在CollectionView的数据源方法里,把对应位置的价格直接赋值给Cell即可:

extension TopUpViewController: UICollectionViewDataSource {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return topUpProducts.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TopUpMoneyCell", for: indexPath) as! TopUpMoneyCell
        let productModel = topUpProducts[indexPath.item]
        
        // 直接给Cell的Label赋值预格式化好的价格
        cell.moneyLabel.text = productModel.localizedPrice
        
        // 如果后续购买需要用到SKProduct,也可以把产品对象传给Cell
        cell.associatedProduct = productModel.product
        
        return cell
    }
}

3. 处理Cell复用的收尾工作

为了彻底避免旧数据残留,在TopUpMoneyCell里重写prepareForReuse方法,清空Label内容和关联的产品:

class TopUpMoneyCell: UICollectionViewCell {
    @IBOutlet weak var moneyLabel: UILabel!
    var associatedProduct: SKProduct?
    
    override func prepareForReuse() {
        super.prepareForReuse()
        // 清空旧数据,防止复用后显示错误内容
        moneyLabel.text = nil
        associatedProduct = nil
    }
}

为什么这样改更好?

  • 彻底解决了异步请求和Cell复用的冲突,价格不会再跳变
  • 数据逻辑集中在VC里,符合MVC架构,更容易维护和调试
  • 预加载所有产品价格,页面加载更流畅,用户体验更好

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 16:39:08