首页 > 解决方案 > 如何提高 UITableView 的性能

问题描述

我有一个UITableView从 firebase 数据库加载图像。表格中的每个单元格包含三张图片。firestore 查询一次加载三个文档,而表格视图在用户滚动到底部时分页。我遇到的问题是,当我滚动表格视图时,每次到达新单元格时都会出现口吃。每个单元格占用的空间比全屏多一点。这是我试图描述的一个例子:https ://imgur.com/a/xRB6gZg

这是产生这些问题的代码:

func paginate(){
    postQuery = postQuery.start(afterDocument: documents.last!)
    self.loadPosts()
}

//queries Firestore and loads into postArray
func loadPosts() {
    if let blockedArray = userDefaults.array(forKey: blockKey) as? [String]{
        blockedUsers = blockedArray
    }
    postQuery.getDocuments{ [weak self](querySnapshot, error) in
        self!.q.async{
            if let err = error {
                print(err)
            }else{
                var postsTemp = self?.postArray
                for doc in querySnapshot!.documents{
                    self?.documents += [doc]
                    let post = self!.createPost(doc)
                    if(!self!.postArray.contains(post) && !self!.blockedUsers.contains(post.uid)){
                        postsTemp?.append(post)
                    }
                     
                    DispatchQueue.main.async {
                        self!.postArray = postsTemp!
                        self!.tableView.reloadData()
                        self!.isNewDataLoading = false
                    }
                }
                self!.loadedFirst = true
            }
        }
    }
}
    
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if postArray.count == 0{
        return 1
    }else{
        return postArray.count
    }
}
   
    
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    var post: PostStruct
    var peopleUserIsFollowing: [String] = []
        
    let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath) as! PostCell
    cell.delegate = self
    if postArray.count == 0 {
        let instructions = cell.textLabel
        instructions?.text = "Press the camera to start Piking!"
        instructions?.textAlignment = .center
        clearPosts(cell)
    }else {
        post = postArray[indexPath.row]
        if let leftPostArray = userDefaults.array(forKey: fbLeftKey) as? [String]{
           votedLeftPosts = leftPostArray
        }
        if let rightPostArray = userDefaults.array(forKey: fbRightKey) as? [String]{
            votedRightPosts = rightPostArray
        }
            
        let firstReference = storageRef.child(post.firstImageUrl)
        let secondReference = storageRef.child(post.secondImageUrl)
            
        //For FriendsTableView query
        let db = Firestore.firestore()
        let followingReference = db.collection("following")
            .document(currentUser!)
            .collection("UserIsFollowing")
        followingReference.getDocuments(){(querySnapshot, err) in
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                for document in querySnapshot!.documents {
                    peopleUserIsFollowing.append(document.documentID)
                }
            }
        }
            
        //Fill in labels and imageViews
            
        cell.timer = createTimer(post, cell)
            
        cell.firstImageView.sd_setImage(with: firstReference)
        cell.secondImageView.sd_setImage(with: secondReference)
            
        cell.leftTitle.text = post.firstTitle
        cell.rightTitle.text = post.secondTitle
            
        cell.postDescription.text = post.postDescription + "\(indexPath)"
        if post.userPic == "" {
            userPic =
                    "https://firebasestorage.googleapis.com/v0/b/pikit-7e40e4.appspot.com/o/Default%20Profile%20Pic.png?alt=media&token=2bc88382-2ad3-4eb8-8163-dcddf391c666"
        } else{
            userPic = post.userPic
        }
        let url = URL(string: userPic)
        let data = try? Data(contentsOf: url!) 
        cell.profilePic.image = UIImage(data: data!)
            
        let votesCollection = db.collection("votes").document(post.postID)
        getCount(ref: votesCollection, cell: cell)
            
        if(post.uid != currentUser){
            cell.userName.text = post.poster
        }else{
            cell.userName.text = "Me"
            cell.tapLeft.isEnabled = false
            cell.tapRight.isEnabled = false
        }
        cell.textLabel?.text = ""
            
        if(post.poster == Auth.auth().currentUser!.uid || post.endDate - Int(NSDate().timeIntervalSince1970) <= 0){
            cell.tapRight.isEnabled = false
            cell.tapLeft.isEnabled = false
            cell.firstImageView.layer.borderWidth = 0
            cell.secondImageView.layer.borderWidth = 0
        }
        else if(votedRightPosts.contains(post.firstImageUrl)){
            cell.secondImageView.layer.borderColor = UIColor.green.cgColor
            cell.secondImageView.layer.borderWidth = 4
            cell.firstImageView.layer.borderWidth = 0
            cell.tapRight.isEnabled = false
            cell.tapLeft.isEnabled = true
        }
        else if (votedLeftPosts.contains(post.firstImageUrl)){
            cell.firstImageView.layer.borderColor = UIColor.green.cgColor
            cell.firstImageView.layer.borderWidth = 4
              
            cell.secondImageView.layer.borderWidth = 0
            cell.tapLeft.isEnabled = false
            cell.tapRight.isEnabled = true
        }
            
    }
        
    return cell
}
    
override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    let postCell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath) as! PostCell
    clearPosts(postCell)
    postCell.timer?.invalidate()
    postCell.timer = nil
}
    
    
    
override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    //Bottom Refresh
        
    if scrollView == tableView{
            
        if ((scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height)
        {
            if !isNewDataLoading{
                isNewDataLoading = true
                paginate()
            }
        }
    }
}

我试过调整什么didEndDisplaying,比如清除细胞/不清除细胞,但这没有效果。我也尝试过改变调用 paginate 的位置,但这似乎是最好的方法。我不确定我哪里出错了。我还在 Xcode 调试器中注意到,随着表格视图的上下滚动,应用程序的内存使用量稳步上升,但似乎从未下降。

标签: iosswiftuitableviewgoogle-cloud-firestore

解决方案


通常,您有两种选择来解决此问题。要解析的代码很多,所以我不能给你一个代码示例,但答案是:

预取

当您滚动到第 2 项时,请在向下滚动之前开始获取第 4、5、6 项(因为您一次获取 3 个)。

另外...您可能会考虑一次获取超过 3 个。比如... 50 或 100。现代 iOS 设备有很多内存。我想不出将其限制在这么少的真正理由。

占位符

构建您的布局,以便它提供占位符数据,然后异步启动获取以使用真实数据更新屏幕布局。

无论哪种方式都需要您稍微重构一下代码。我的直觉说,预取对你来说会更容易。


推荐阅读