【问题标题】:Swift tableView.reloadData() does not reload table view in SwiftSwift tableView.reloadData() 不会在 Swift 中重新加载表视图
【发布时间】:2021-07-18 10:21:27
【问题描述】:

尝试在获取请求后重新加载我的 tableView,但是它不起作用。 我在第一个 tableViewCell 中有嵌入 collectionView 的 tableView。 这是我的 collectionView 类的样子:

class RegionCell: UITableViewCell, FetchRegionsDelegate {
    
    static let reuseId = "regionCellID"
    var collectionView: UICollectionView!
    var tableViewManager = RegionsAndCountriesTableVC()
    var selectedRegion: String?

    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        configureCollectionView()
        tableViewManager.delegate = self
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func configureCollectionView() {
        collectionView = UICollectionView(frame: contentView.bounds, collectionViewLayout: createThreeColumnFlowLayout(in: contentView))
        contentView.addSubview(collectionView)
        collectionView.fillSuperview()
        collectionView.delegate   = self
        collectionView.dataSource = self
        collectionView.backgroundColor = .systemBackground
        collectionView.register(RegionCollectionViewCell.self, forCellWithReuseIdentifier: RegionCollectionViewCell.reuseId)
    }
    
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        6
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RegionCollectionViewCell.reuseId, for: indexPath) as! RegionCollectionViewCell
        let region = regions[indexPath.item]
        cell.imageView.image = region.regionImage
        cell.textLabel.text = region.regionName
        if indexPath.row == 0 {
            collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .left)
            cell.isSelected = true
        }
        return cell
    }

    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        tableViewManager.fetchByRegions(regionName: (regions[indexPath.item].regionName).lowercased())
            self.tableViewManager.tableView.reloadData()
    }

当我选择 didSelectItemAt 时,委托工作并且在 UITableViewController 中执行一个带有新 API 调用的函数,然而,即使 API 调用成功,tableView 单元格保持不变,即使在 self.tableView.reloadData() 之后叫做。这是我的 UITableViewController 类的外观:

protocol FetchRegionsDelegate {
    var selectedRegion: String? { get set }
}


class RegionsAndCountriesTableVC: UITableViewController {
    
    var countries = [Country]()
    var delegate: FetchRegionsDelegate?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(CountryCell.self, forCellReuseIdentifier: CountryCell.reuseId)
        tableView.register(RegionCell.self, forCellReuseIdentifier: RegionCell.reuseId)
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        fetchInitialData()
    }


    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 2
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        section == 0 ? 1 : countries.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if indexPath.section == 0 {
            let cell = tableView.dequeueReusableCell(withIdentifier: RegionCell.reuseId, for: indexPath)
            return cell as! RegionCell
        } else {
            let cell = tableView.dequeueReusableCell(withIdentifier: CountryCell.reuseId, for: indexPath) as! CountryCell
            cell.textLabel?.text = countries[indexPath.row].name
            return cell
        }
    }
    
    override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        section == 0 ?  "Regions" : "Country"
    }
    
    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        indexPath.section == 0 ? 250 : 45
    }

    // MARK: - FetchRequest
    
    func fetchInitialData() {
        let url = "https://restcountries-v1.p.rapidapi.com/all/?rapidapi-key=APIKey"
        fetchGenericJSONData(urlString: url) { (countries: [Country]? , error) in
            guard let safeCountries = countries else { return }
            self.countries = safeCountries
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }
    }
    
    func fetchByRegions(regionName: String) {
        if regionName == "all" {
            countries.removeAll()
            fetchInitialData()
        } else {
            countries.removeAll()
            print(self.countries.count)
            let url = "https://restcountries-v1.p.rapidapi.com/region/\(regionName)/?rapidapi-key=APIKey"
            fetchGenericJSONData(urlString: url) { (countries: [Country]?, error) in
                guard let safeCountriesByRegion = countries else { return }
                
                self.countries = safeCountriesByRegion
                print(self.countries.count)
                print(self.countries)
                DispatchQueue.main.async {
                    self.tableView.reloadData()
                }
            }
        }
    }

}

谁能告诉我我在这里做错了什么? 提前谢谢!

【问题讨论】:

  • “它不起作用”请准确说明您的期望以及您使用当前代码获得的结果
  • 在故事板环境中RegionsAndCountriesTableVC() 创建一个全新的控制器实例,它不是故事板中的实例。因此,您在 10 个不同的单元格中获得了 10 个不同的实例。您需要对表视图控制器的 real 引用。顺便说一句,回调闭包比协议/委托更有效,更可取。
  • @Jerem Lachkar 我的期望是,当我在 RegionCell 中通过 didSelectItem 选择区域时,我的 tableView 将重新加载其数据,因此,用户将只能看到所选区域的国家/地区。基本上,我只需要了解为什么 tableView.reloadData 没有更新表格。所有对 API 的请求都可以正常工作,我可以看到,在我调用 tableView.reloadData() 之前,该国家/地区数组已填充了所需的国家/地区
  • @vadian 我在这个项目中没有使用故事板,它被完全删除了。但是,您对闭包的效率可能是正确的,但问题是我是编程新手,我不太清楚如何通过闭包实现这一点。

标签: ios swift uitableview uicollectionview delegates


【解决方案1】:

在表格视图单元格中创建与父视图控制器无关的 RegionsAndCountriesTableVC 新实例不是您想要的,也是问题的原因。

您需要对父控制器的实际引用。

这可以通过回调闭包非常简单地完成。


RegionCell中定义一个回调属性,传递区域名

class RegionCell: UITableViewCell{

   var callback : ((String) -> Void)?
...

并调用它而不是委托

 func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let regionName = regions[indexPath.item].regionName.lowercased()
    callback?(regionName)
 }

删除与协议/委托相关的整个代码。


RegionsAndCountriesTableVC 中分配cellForRow 中的回调

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if indexPath.section == 0 {
        let cell = tableView.dequeueReusableCell(withIdentifier: RegionCell.reuseId, for: indexPath) as! RegionCell
        cell.callback = { regionName in
            self.fetchByRegions(regionName: regionName)
            tableView.reloadData()
        }
        return cell 
    } else {
        let cell = tableView.dequeueReusableCell(withIdentifier: CountryCell.reuseId, for: indexPath) as! CountryCell
        cell.textLabel?.text = countries[indexPath.row].name
        return cell
    }
}

【讨论】:

  • 非常感谢@vadian!!!感谢您的第一条评论,我刚刚解决了代表的问题,但是,想法是相同的,您将我推向所需的方向)非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-08
  • 1970-01-01
  • 2017-01-06
  • 1970-01-01
  • 2018-12-24
  • 2015-09-21
  • 1970-01-01
相关资源
最近更新 更多