Swift开发购物App时UITableView cell赋值出现Index out of range错误
Swift购物App UITableView索引越界问题解决方案
核心问题原因
触发Index out of range的本质是UITableView要求渲染的行数,大于对应数据源数组的元素个数,从给出的代码来看,有三个直接触发问题的原因:
numberOfRowsInSection方法返回值和数据源数组长度不匹配:如果该方法返回了固定数值、或者返回了长度更大的其他数组的count,就会出现tableView要渲染第N行,但namee/myprice/mySecond数组没有第N个元素的情况。- 多数据源数组不同步:解析逻辑只更新了
namee、myprice、myphoto三个数组,但cell赋值时用到了mySecond数组,若没有在解析时同步更新mySecond数组的元素,就会出现数组长度不一致,访问越界。 - 强制解包、同步加载网络图片的隐性风险:代码中大量使用
!强制解包、try!执行抛出方法,一旦数据不符合预期就会直接崩溃,同步加载网络图片还会卡住主线程影响交互。
修复步骤
步骤1:修正行数返回逻辑
确保numberOfRowsInSection返回值和用来做渲染的数据源数组长度完全一致:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // 如果继续用多个独立数组,返回所有数组中长度最小的值,优先建议替换为单数据源数组 return namee.count }
步骤2:修复数据源同步问题
统一替换为单数据源数组,避免多数组不同步的问题:
首先定义统一的商品模型:
struct GoodsModel: Codable { let name: String let price: String let imageUrl: URL } // 全局只维护一个数据源数组 var goodsList: [GoodsModel] = []
调整解析逻辑:
func extracted(){ guard let url = URL(string: "http://rajeshrmohan.com/sport.json") else { return } let task = URLSession.shared.dataTask(with: url){ (data,response,error) in guard let dataResponse = data, error == nil else { print(error?.localizedDescription ?? "Response Error") return } do { let decoder = JSONDecoder() let model = try decoder.decode(FullClothes.self, from: dataResponse) // 清空原有数据避免重复添加 self.goodsList.removeAll() for item in model.item { // 可选绑定过滤无效数据,避免崩溃 guard let name = item.name, let price = item.price, let imageStr = item.image, let imageUrl = URL(string: imageStr) else { continue } self.goodsList.append(GoodsModel(name: name, price: String(price), imageUrl: imageUrl)) } DispatchQueue.main.async { self.tableView.reloadData() } } catch let parsingError { print("Error", parsingError) } } task.resume() }
步骤3:优化cell赋值逻辑
移除强制解包,改用异步加载图片,增加数组越界防护:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "hello", for: indexPath) as! DisplayShopTableCellTableViewCell // 越界防护 guard indexPath.row < goodsList.count else { return cell } let goods = goodsList[indexPath.row] cell.userLbl.text = goods.name cell.passLbl.text = goods.price // 异步加载图片,避免卡主线程和强制崩溃 DispatchQueue.global().async { if let imageData = try? Data(contentsOf: goods.imageUrl), let image = UIImage(data: imageData) { DispatchQueue.main.async { // 防止cell复用出现图片错乱 if tableView.indexPath(for: cell) == indexPath { cell.tablecellimageview.image = image } } } } return cell }
内容的提问来源于stack exchange,提问作者KAKEN
相关产品推荐
相关产品推荐

