numberOfRowsInSection 已调用但 cellForRowAt 未调用

Posted

技术标签:

【中文标题】numberOfRowsInSection 已调用但 cellForRowAt 未调用【英文标题】:numberOfRowsInSection called but cellForRowAt not called 【发布时间】:2021-05-27 13:31:02 【问题描述】:

在我的应用程序中,我调用了一个 API 来获取一些数据并从中下载文件。下载后回调调用。在这种情况下,表格视图更新得很好。但是假设用户访问了屏幕并且在将内容下载回前一个屏幕并再次访问同一屏幕之前。在这种情况下,表视图未更新。仅当用户在未完成下载内容的情况下返回上一个控制器并再次出现在同一屏幕上时,我才会遇到此问题。

WarrantyVC.Swift

class WarrantyVC: UIViewController 
    
    // MARK: - Outlets
    ///
    @IBOutlet weak var warrantyTableView: UITableView!
    
    //Latum
    ///
    @IBOutlet weak var knowMoreButton: UIButton!
    
    // MARK: - Variables
    ///
    var warrantyDataSource: WarrantyDataSource!
    ///
    var viewModel: WarrantyViewModel?
   
    // MARK: - Controller life cycle
    ///
    override func viewDidLoad() 
        super.viewDidLoad()
        setupUI()
       getUserGuide()
    
    

    ///
    deinit 
        viewModel = nil
    
    
    // MARK: - Initializing UI methods
    ///
    func setupUI() 
        viewModel = WarrantyViewModel()
        warrantyDataSource = WarrantyDataSource(withTableView: warrantyTableView)
        warrantyDataSource.delegate = self
    
    
    // MARK: - API methods
    ///
    func getUserGuide() 
        CommonMethods.showProgressHud(inView: self.view)
        warrantyDataSource.introVideoAndGuideArray.removeAll()
        viewModel?.getUserGuidefunc(userId: "\(DataManager.shared.user?.userId ?? 0)", motorId: selectedMotor?.MotorTypeId ?? 0,dmpModelId:selectedMotor?.dmpModelId ?? 0)  [self] (success, response) in
            CommonMethods.hideProgressHud()
            Console.log(response ?? [String: Any]())
            self.setupUI()
            if !success  /Show some message./ 
            let userGuides = UserGuideService.introVideoAndGuideArray
            if userGuides.count > 0 
                var newUserGuideArray = [UserGuide]()
                for tempUserGuide in userGuides 
                    let userGuide = tempUserGuide
                    let userManual = userGuide.usermanual
                    let videolink = userGuide.videolink
                    if videolink.trim().count > 0 
                        if let tempUserGuide = userGuide.copy() as? UserGuide 
                            tempUserGuide.usermanual = ""
                            tempUserGuide.videolink = videolink
                            newUserGuideArray.append(tempUserGuide)
                        
                    
                    if userManual.trim().count > 0 
                        if let tempUserGuide = userGuide.copy() as? UserGuide 
                            tempUserGuide.usermanual = userManual
                            tempUserGuide.videolink = ""
                            newUserGuideArray.append(tempUserGuide)
                        
                    
                
                 self.warrantyDataSource.introVideoAndGuideArray = newUserGuideArray
                DispatchQueue.main.async 
                    self.warrantyTableView.reloadData()
                
            
        
    

WarrantyDatasource.swift

protocol UserGuideDataSourceDelegate: class 
    ///
    func onOpenPDF()
    ///
    func onPlayVideo(_ videoURL: String)


/// This DataSource class use to show data of UserGuide in table.
class WarrantyDataSource: NSObject 
    
    // MARK: - Variables
    ///
    fileprivate let cellNibName = "warrantyCell"
    ///
    var userGuideTitle: [String] = ["Intro to ABC", “XYZ”]
    ///
    var userGuideImage: [UIImage] = [ imageLiteral(resourceName: "ic_play"),  imageLiteral(resourceName: "ic_pdf")]
    ///
    fileprivate let cellSpacingHeight: CGFloat = 12
    ///
    var introVideoAndGuideArray = [UserGuide]()
    ///
    weak var delegate: UserGuideDataSourceDelegate?
    ///
    var numberOfSections: Int = 0
    ///
    convenience init(withTableView tableView: UITableView) 
        self.init()
        tableView.delegate = self
        tableView.dataSource = self
    
    
    ///
    @objc func onOpenPDF(_ sender: UIButton) 
        guard introVideoAndGuideArray.count > 0 else  return 
        delegate?.onOpenPDF()
    
    
    ///
    @objc func onPlayVideo(_ sender: UIButton) 
        delegate?.onPlayVideo(introVideoAndGuideArray[sender.tag].videolink)
    
    
    /// This function help to setup user menual cell
    ///
    /// - Parameters:
    ///   - cell: UserGuideCell
    ///   - indexPath: IndexPath
    func setUserGuidePDFCell(_ cell: WarrantyCell, _ indexPath: IndexPath) 
        cell.guideImageView.image =  imageLiteral(resourceName: "ic_default_pdf")
        guard introVideoAndGuideArray.count > 0 else  return 
        cell.guideLabel.text = introVideoAndGuideArray[indexPath.section].userGuideTitle
        cell.guideButton.setImage(userGuideImage[1], for: .normal)
        cell.guideButton.tag = indexPath.section
        cell.guideButton.addTarget(self, action: #selector(onOpenPDF(_:)), for: .touchUpInside)
    


// MARK: - Extension Implementing UITableView DataSource Callbacks
///
extension WarrantyDataSource: UITableViewDataSource 
    // MARK: Extension Implementing UITableView DataSource methods
    ///
    func numberOfSections(in tableView: UITableView) -> Int 
        numberOfSections = introVideoAndGuideArray.count
        return introVideoAndGuideArray.count
    
    ///
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 1
    
    
    /// Set the spacing between sections
    func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat 
        return cellSpacingHeight
    
    
    /// Make the background color show through
    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? 
        let headerView = UIView()
        headerView.backgroundColor = UIColor.clear
        return headerView
    
    
    ///
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellNibName, for: indexPath) as? WarrantyCell else  return WarrantyCell() 
        if NetworkManager.sharedInstance.isReachable 
            let userGuide = introVideoAndGuideArray[indexPath.section]
            if userGuide.videolink.trim().count > 0 
                cell.guideButton.setImage(userGuideImage[0], for: .normal)
                cell.guideButton.tag = indexPath.section
                cell.guideButton.addTarget(self, action: #selector(onPlayVideo(_:)), for: .touchUpInside)
                cell.guideLabel.text = introVideoAndGuideArray[indexPath.section].videoTitle
                let videoThumbnail = introVideoAndGuideArray[indexPath.section].vehicleVideoThumbnailLink
                cell.guideImageView.kf.setImage(with: URL.init(string: videoThumbnail), placeholder: nil, options: nil, progressBlock: nil, completionHandler: nil)
             else 
                setUserGuidePDFCell(cell, indexPath)
            
         else 
            setUserGuidePDFCell(cell, indexPath)
        
        return cell
    

【问题讨论】:

好吧,如果你弹出了一个 viewController,你应该取消正在运行的任务并在推送时再次调用 API,但是如果你正在持久化 API 结果,那么你可以在本地获取记录并在第二次推送时显示,如果是这样的话在需求范围内。 “数据没有重新开始下载”,它应该开始并且它不依赖于你之前的任务。您的代码中可能还有其他问题。 【参考方案1】:

当您第一次调用 getUserGuide API 并且在获取回调之前或在下载数据之前,您会弹出视图控制器并在同一控制器上再次推送。第二次 getUserGuide API 将调用,您将从第一次调用 API 请求中获得回调。因此产生了这个问题。

您可以通过如下更改实现来解决此问题;

当第一次调用 API 并且在下载完成之前用户尝试弹出视图控制器暂停下载时,将响应中的 resumeData 存储在 userdefault 中。当用户第二次尝试调用 API 时,检查您已经启动下载的相同链接。如果然后通过从用户默认值中提取恢复数据来恢复相同链接的下载。

我希望这种方式对你有用。快乐编码。

【讨论】:

以上是关于numberOfRowsInSection 已调用但 cellForRowAt 未调用的主要内容,如果未能解决你的问题,请参考以下文章

未调用 cellForRowAtIndexPath 但调用 numberOfRowsInSection

cellForRowAtIndexPath 在比 numberOfRowsInSection 更晚被调用后崩溃

numberOfRowsInSection:未在 reloadData 上调用

未调用 cellForRowAt 但正在调用 numberOfSection 和 numberOfRowsInSection

UITableView numberOfRowsInSection 被多次调用

在 numberOfRowsInSection 中调用 TableView BeginUpdates