我怎样才能提高我的 UITableView 的性能
How can I improve the performace of my UITableView
我有一个 UITableView
从 firebase 数据库加载图像。 table 中的每个单元格包含三张图片。 firestore 查询一次加载三个文档,而 table 视图在用户滚动到底部时分页。我遇到的问题是,当我滚动 table 视图时,每次它到达一个新单元格时都会断断续续。每个单元格占用的空间比整个屏幕多一点。这是我要描述的示例: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
的功能,例如清除单元格/不清除单元格,但没有效果。我也尝试过改变调用分页的位置,但这似乎是最好的方法。我不确定哪里出错了。我还在 Xcode 调试器中注意到,随着 table 视图上下滚动,应用程序的内存使用量稳步上升,但似乎永远不会下降。
一般来说,您有两种选择来解决这个问题。有很多代码需要解析,所以我不能给你一个代码示例,但答案是:
预取
当您滚动到第 2 项时,在向下滚动到第 2 项之前开始获取第 4、5、6 项(因为您一次获取 3 个)。
另外...您可以考虑一次获取 3 个以上。就像... 50 或 100。现代 iOS 设备有很多内存。我想不出真正的理由将它限制在这么少的范围内。
占位符
构建您的布局,使其提供占位符数据,然后异步启动获取以使用真实数据更新屏幕布局。
无论哪种方式都需要您稍微重构一下代码。我的直觉告诉你预取会更容易。
我有一个 UITableView
从 firebase 数据库加载图像。 table 中的每个单元格包含三张图片。 firestore 查询一次加载三个文档,而 table 视图在用户滚动到底部时分页。我遇到的问题是,当我滚动 table 视图时,每次它到达一个新单元格时都会断断续续。每个单元格占用的空间比整个屏幕多一点。这是我要描述的示例: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
的功能,例如清除单元格/不清除单元格,但没有效果。我也尝试过改变调用分页的位置,但这似乎是最好的方法。我不确定哪里出错了。我还在 Xcode 调试器中注意到,随着 table 视图上下滚动,应用程序的内存使用量稳步上升,但似乎永远不会下降。
一般来说,您有两种选择来解决这个问题。有很多代码需要解析,所以我不能给你一个代码示例,但答案是:
预取
当您滚动到第 2 项时,在向下滚动到第 2 项之前开始获取第 4、5、6 项(因为您一次获取 3 个)。
另外...您可以考虑一次获取 3 个以上。就像... 50 或 100。现代 iOS 设备有很多内存。我想不出真正的理由将它限制在这么少的范围内。
占位符
构建您的布局,使其提供占位符数据,然后异步启动获取以使用真实数据更新屏幕布局。
无论哪种方式都需要您稍微重构一下代码。我的直觉告诉你预取会更容易。