【问题标题】:iOS app is crashing on scroll while fetching data from API从 API 获取数据时,iOS 应用程序在滚动时崩溃
【发布时间】:2020-09-02 15:00:50
【问题描述】:

我使用 tableView 来显示帖子,当我滚动页面时,它有时会崩溃并抛出错误:由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:'无效更新:无效的行数第 0 节。更新后现有节中包含的行数 (119) 必须等于更新前该节中包含的行数 (105),加上或减去从该节中插入或删除的行数(1 个插入,1 个删除)加上或减去移入或移出该部分的行数(0 移入,0 移出)。'我无法找出它发生的原因 p>

func getFeeds(){
    DispatchQueue.main.async {
        if(self.isCommentsOpened == false && self.spinner == nil && !self.isPaginating && self.isScrollingUp == false){
        self.spinner = CommonUtils.showLoader(view: self)
        }
    }
     var paramsDictionary = [String:Any]()
            let xAuthToken:String=UserDefaults.standard.string(forKey: JsonContants.X_AUTH_TOKEN)!
            let entityId:String=UserDefaults.standard.string(forKey: JsonContants.ENTITY_ID)!
            var apiUrl=domains.global+Apis.feedQueryApi
            apiUrl=apiUrl.replacingOccurrences(of: "{entity_id}", with: entityId)
            if(isPaginating && pageNo<totalPages){
            paramsDictionary[JsonContants.PAGE_NO] = pageNo+1
            paramsDictionary[JsonContants.PAGE_SIZE] = pageSize
            }else{
                paramsDictionary[JsonContants.PAGE_NO] = 1
                paramsDictionary[JsonContants.PAGE_SIZE] = pageSize
            }
            HttpClientApi.instance().makeAPICall(token: xAuthToken,refreshToken: "",url:  apiUrl, params:paramsDictionary, method: .POST, success: { (data, response, error) in
                do {
                    if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] {
                        let httpStatus=response as HTTPURLResponse!
                        let statusCode=httpStatus?.statusCode
                        let pageDetails = json[JsonContants.PAGE_DETAILS] as! [String:Any]
                        if(pageDetails.count>0){
                            self.pageNo =  pageDetails[JsonContants.PAGE_NO] as! Int
                            self.pageSize =  pageDetails[JsonContants.PAGE_SIZE] as! Int
                            self.totalPages = pageDetails[JsonContants.TOTAL_PAGES] as! Int
                        }
                        let feedsArray = json[JsonContants.FEEDS] as! Array<Dictionary<String, Any>>
                        if(!self.isPaginating){
                            self.feedsJsonArray = feedsArray
                            self.feedIdsArray = [String]()
                            self.feedIdUserIdDict = [String:String]()
                            self.userIdUserJsonDict = [String:[String:Any]]()
                            self.imagesJsonArray = []
                            self.feedIdActivityJsonDict = [String:[[String:Any]]]()
                        }else{
                            self.paginationFeedIdsArray = [String]()
                            self.feedsJsonArray.append(contentsOf: feedsArray)
                        }
                        var paginationUserIdDict = [String]()
                        for object:Dictionary<String,Any> in feedsArray{
                            let feedId=object[JsonContants.FEED_ID] as? String
                            self.feedIdsArray.append(feedId!)
                            let userId = object[JsonContants.USER_ID] as? String
                            self.feedIdUserIdDict[feedId!] = userId
                            if(self.isPaginating){
                            paginationUserIdDict.append(userId!)
                            self.paginationFeedIdsArray.append(feedId!)
                            }
                        }
                         DispatchQueue.main.async {
                        if(!self.isPaginating){
                        self.getUserList(userIds: Array(self.feedIdUserIdDict.values))
                            self.dismissLoader()
                        }else{
                            self.getUserList(userIds: paginationUserIdDict)
                            self.dismissLoader()
                            }
                        }
                    }
                } catch let error {
                    print(error.localizedDescription)
                    DispatchQueue.main.async {
                        if(self.spinner != nil){
                        self.spinner!.dismissLoader()
                        self.spinner = nil
                        }
                        if((self.topScrollSpinnerContainerView.isHidden == false) ||  (self.downScrollSpinnerContainerView.isHidden == false)){
                            self.scrollSpinner!.dismissLoader2()
                            self.topScrollSpinnerContainerView.isHidden = true
                            self.downScrollSpinnerContainerView.isHidden = true
                        }
                    }
                }
                
            }, failure: { (data, response, error) in
                DispatchQueue.main.async {
                    if(self.spinner != nil){
                    self.spinner!.dismissLoader()
                    self.spinner = nil
                    }
                    if((self.topScrollSpinnerContainerView.isHidden == false) ||  (self.downScrollSpinnerContainerView.isHidden == false)){
                        self.scrollSpinner!.dismissLoader2()
                        self.topScrollSpinnerContainerView.isHidden = true
                        self.downScrollSpinnerContainerView.isHidden = true
                    }
                }
                
                print(error?.localizedDescription as Any)
                
            })
}

func showSmallLoaderAtBottom(view: UIView) -> UIView {

        var spinnerView = UIView(frame: CGRect(x: 0, y: 0, width: 50, height:50))
        let spinner = MDCActivityIndicator(frame: CGRect(x: 0, y: 0, width: 50, height:50))
        spinner.cycleColors = [UIColor(named: AppColors.appSkyBlue.rawValue)!, UIColor(named: AppColors.appSkyBlue.rawValue)!,UIColor(named: AppColors.appSkyBlue.rawValue)!,UIColor(named: AppColors.appSkyBlue.rawValue)!]
        spinnerView.addSubview(spinner)
        view.addSubview(spinnerView)
        spinner.startAnimating()
        return spinnerView
        }

func getMoreData(){
    if(self.pageNo < self.totalPages){
        scrollSpinner = showSmallLoaderAtBottom(view: downScrollSpinnerContainerView)
        downScrollSpinnerContainerView.isHidden = false
        isPaginating = true
        self.getFeeds()
    }
}

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    self.currentRow = indexPath.row
    let cell=tableView.dequeueReusableCell(withIdentifier: "feed_tvcell", for: indexPath) as! FeedTableViewCell
    cell.selectionStyle = .none
    cell.feedViewController = self
    cell.feedsStackView.translatesAutoresizingMaskIntoConstraints = false
    
    let feedJson = self.feedsJsonArray[indexPath.row]
    cell.messageTextLabel.numberOfLines = 0
    cell.messageTextLabel.lineBreakMode = .byWordWrapping
    cell.messageTextLabel.sizeToFit()
    if(feedJson[JsonContants.CONTENT] != nil && !(feedJson[JsonContants.CONTENT] as! String).elementsEqual(JsonContants.NA)){
        cell.feedMessageView.isHidden = false
        cell.messageTextLabel.text = (feedJson[JsonContants.CONTENT] as! String).decodeEmoji
    }else{
        cell.feedMessageView.isHidden = true
    }
    let feedId = feedJson[JsonContants.FEED_ID] as! String
    let userId = feedIdUserIdDict[feedId] as! String
    let userJson = userIdUserJsonDict[userId] as? [String:Any]
    var userTitle:[[String:Any]]?
    var lastName:String?
    var firstName:String?
    if(userJson != nil){
        userTitle = userJson![JsonContants.USER_TITLE] as? [[String:Any]]
        lastName = userJson![JsonContants.LAST_NAME] as? String
        firstName = userJson![JsonContants.FIRST_NAME] as? String
    }
    var roles = self.appDefaults.value(forKey: JsonContants.ROLE) as! [String]
    if(firstName != nil){
    cell.feeUserName.text = (firstName as! String)
    }
    if(lastName != nil){
        cell.feeUserName.text = cell.feeUserName.text!+" "+(lastName as! String)
    }
    
    hideAndShowFeedDeleteButton(roles, cell, userId)
    
    addActionTargetsToCellButtons(cell)
    
    var time = feedJson[JsonContants.LAST_ACTIVITY_TIME] as! Int64
    var timeDiff = CommonUtils.getSystemTimeInMillis() - time
    cell.feedTime.text = CommonUtils.getStringValueOfTimeDiff(timeDiff: timeDiff)
    
    setCountZero(cell)
    setLikeCount(feedId, cell, feedJson)
    
    var feedType = feedJson[JsonContants.TYPE] as! String
    
    let feedCommentCount = feedJson[JsonContants.FEED_COMMENT_COUNT] as! Int
    cell.commentCountLabel.text = String(feedCommentCount)
    
    
    if(userJson != nil){
        let userId = userJson![JsonContants.USER_ID] as? String
        var cachedMediaId:UIImage?
        let userJsonFromDb = CoreDataDatabaseManager.coreDataDatabaseManager.getUser(id: userId!)
        if(userJsonFromDb != nil && userJsonFromDb.count>0){
        let imageId = userJsonFromDb[JsonContants.IMAGE_ID] as? String
        if(userId != nil && imageId != nil){
            cachedMediaId  = self.getSavedImage(named : imageId!+"."+"png")
            }
        }
        if(cachedMediaId == nil){
            cell.feedUserImage.image = #imageLiteral(resourceName: "user_default_round")
            getUserImages(userIds: [userId!],cell: cell)
            
        }else{
            cell.feedUserImage.image = cachedMediaId
        }
      
    }else{
        cell.feedUserImage.image = #imageLiteral(resourceName: "user_default_round")
    }
    
    
    if(userJson != nil && userJson![JsonContants.RESIDENT_DETAILS] != nil){
        let residentDetails = userJson![JsonContants.RESIDENT_DETAILS] as! [String:Any]
        let buildingName = residentDetails[JsonContants.BUILDING_NAME] as! String
        let flatName = residentDetails[JsonContants.FLAT_NAME] as! String
        let feedUserName:String = cell.feeUserName.text!
        if(buildingName.count>0 && flatName.count>0){
        cell.feeUserName.text = feedUserName+" - "+buildingName+"("+flatName+")"
        }
    }else if(userTitle != nil && userTitle!.count>0) {
        let titleDict = userTitle![0]
        let name = cell.feeUserName.text
        if(titleDict.count>0){
            var title = titleDict[JsonContants.TITLE] as! String
            if(title.elementsEqual(JsonContants.ADMINISTRATOR)){
            cell.feeUserName.text = name!+" - "+"Admin"
            }
        }
    }
    if(feedType.elementsEqual(JsonContants.SURVEY)){
        let options = feedJson[JsonContants.OPTIONS] as! [[String:Any]]
        setSurveyData(cell, options, feedId, feedJson,indexPath: indexPath)       
        
    }else{
    
    if(feedJson[JsonContants.MEDIA_INFO] != nil && (feedJson[JsonContants.MEDIA_INFO] as! [String:Any]) != nil &&
        (feedJson[JsonContants.MEDIA_INFO] as! [String:Any]).count>0){
       let  mediaJson = feedJson[JsonContants.MEDIA_INFO] as! [String:Any]
        let mediaType = mediaJson[JsonContants.MEDIA_TYPE] as! String
        cell.optionsTableView.isHidden = true
        cell.surveyTotalVotesLabel.isHidden = true
        cell.surveyTotalVotesLabelContainer.isHidden = true
        cell.feedVideoView.isHidden = true
        cell.feedImageView.isHidden = true
        
        if(mediaType.elementsEqual(JsonContants.IMAGE)){
            configureFeedImageViewAndSetImage(cell, mediaJson, feedId,indexPath)
        }else{
            configureFeedMediaViewAndSetVideo(cell, mediaJson, indexPath)
        }
    }else{
        cell.mediaType = "TEXT"
        hideFeedMediaViews(cell)
        cell.layoutIfNeeded()
    }
    
    }
     if(indexPath.row == (self.feedsJsonArray.count-2)){
                DispatchQueue.main.async{
                    self.getMoreData()
                }
     }
    cell.layoutIfNeeded()
    return cell
}

【问题讨论】:

  • 我在您发布的代码中找不到任何问题;唯一可能导致这种情况的是self.getMoreData() - 你能展示这部分吗?
  • 好的,我也使用该功能进行更新
  • 您需要展示将数据添加到模型/表格视图的代码,例如 getFeeds

标签: ios swift


【解决方案1】:

当您在 TableView 中显式插入或删除单元格或部分并且没有同时重新调整行或部分计数以匹配时,就会出现这种错误。在您的情况下,错误消息准确地告诉您计数不匹配是什么。您发布的代码未显示 TableView 中的任何插入或删除行,因此它必须在您的代码中的其他地方发生,也许在您的 getFeeds() 方法中?

【讨论】:

  • 请再看看
  • 我没有看到任何在表格视图中添加或删除行的代码。它在错误消息中说行数从 105 变为 119。这将是 numberOfRowsInSection 方法返回的值。它还说添加了 1 行,删除了 1 行。此错误消息总是说明它在我的经验中的含义。我会在行计数方法上设置一个断点,并研究为什么在获取新数据后刷新 TableView 后该值发生更改时返回的行数不匹配。我过去就是这样解决的。
  • 看起来上次更新只增加了 14 行,而之前的所有更新都增加了 15 行。我敢打赌你的问题源于此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-27
相关资源
最近更新 更多