TableView的数据不完整 [英] TableView gets laggy with data
问题描述
所以我面临一个问题
重新加载/添加行几次后,表视图变慢(响应于滚动,点击变慢)
A table view gets slower (responses slower to scrolling, tapping) after reloading/adding rows for a couple of times
因此,用户登录后,该应用将下载10个"WorldMessages" .它已加载到此表视图中.
So after the user logs in, the app downloads 10 "WorldMessages". It is loaded into this table view.
如果用户向下滚动,它将调用一个加载更多10的函数:loadOlderOwnWorldMessages()
If the user scrolls down, it calls a function which loads more 10: loadOlderOwnWorldMessages()
每个单元格都有tapGestureRecognizer
+ longPressGestureRecognizer
Each cell has a tapGestureRecognizer
+ longPressGestureRecognizer
我不得不提到,如果用户重新加载tableView,那么它将清除数据并仅再次加载前10个WorldMessages
And I have to mention that If the user reloads the tableView, then it clears the data and loads only the first 10 WorldMessages again
问题
我不知道为什么,但是例如,如果我重新加载tableView 50次,并且每次向下滚动一次或多次,则tableView会变慢.
I don't know why, but for example, if I reload the tableView 50 times and every time I do scroll down a bit or more, then the tableView gets slow.
也许是因为点击/长按手势识别器或约束?
Maybe because of the tap/long press gesture recognizers, or constraints?
应用看起来像这样:
视频延迟后:
https://www.youtube.com/watch?v=65NkjS-Kz3M
(如果我终止了该应用程序并再次将其打开,则它可以再次正常运行,直到我重新加载了几次)
代码:
//不是全部代码,我删除了很多不重要的行(例如重载函数等
// It's not the whole code, I deleted lots of lines of it that were not important (like the reload functions, and etc
class ProfileViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UITabBarDelegate {
// Classes
let handleData = HandleData()
let handleResponses = HandleResponses()
let worldMessagesFunctions = WorldMessagesFunctions()
let profileFunctions = ProfileFunctions()
// View Objects
@IBOutlet var tableView : UITableView!
// Variables
var userWorldMessages = [WorldMessage]()
var lastContentOffsetY : CGFloat?
var currentSelectedWorldMessageIndexPath : IndexPath?
// Main Code
override func viewDidLoad() {
super.viewDidLoad()
userWorldMessages = ownWorldMessages.shared.worldMessages
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return userWorldMessages.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ProfileWorldMessageCell", for: indexPath) as! ProfileWorldMessageCell
let worldMessage = userWorldMessages[indexPath.row]
cell.worldMessageData = worldMessage
cell.messageLabel.text = worldMessage.message
if let image = UIImage(named: "bubble") {
let h = image.size.height / 2
let w = image.size.width / 2
cell.bubbleImageView.image = image
.resizableImage(withCapInsets:
UIEdgeInsetsMake(h, w, h, w),
resizingMode: .stretch).withRenderingMode(.alwaysTemplate)
cell.bubbleImageView.tintColor = appColors.worldMessageBubble
}
let calendar = NSCalendar.current
let date = Date(timeIntervalSince1970: Double(worldMessage.leftTime))
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US")
var dateFormat = "yyyy MMM dd"
if calendar.isDateInToday(date) {
// Today
dateFormat = "HH:mm"
dateFormatter.string(from: date)
} else if (calendar.date(byAdding: .weekOfYear, value: -1, to: Date())! < date){
// This last 7 days
dateFormat = "EEEE HH:mm"
} else if (calendar.date(byAdding: .month, value: -12, to: Date())! < date){
// This year
dateFormat = "MMM dd"
} else {
dateFormat = "yyyy MMM dd"
}
dateFormatter.dateFormat = dateFormat
let strDate = dateFormatter.string(from: date)
cell.timeLabel.text = strDate
cell.commentsButton.setTitle("\(worldMessage.comments!) comments", for: .normal)
cell.likesButton.setTitle("\(worldMessage.likes!) likes", for: .normal)
// tapped
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(bubbleTappedHandler))
cell.bubbleButton.addGestureRecognizer(tapGestureRecognizer)
cell.bubbleButton.isUserInteractionEnabled = true
// long pressed
let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(bubbleLongPressHandler))
longPressGestureRecognizer.minimumPressDuration = 0.5
cell.addGestureRecognizer(longPressGestureRecognizer)
cell.isUserInteractionEnabled = true
return cell
}
var cellHeights: [IndexPath : CGFloat] = [:]
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cellHeights[indexPath] = cell.frame.size.height
if indexPath.row == UserWorldMessagesStore.shared.worldMessages.count - 1 && userWorldMessagesCanLoadMore == true {
loadOlderOwnWorldMessages()
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if cellHeights[indexPath] != nil {
return CGFloat(Float(cellHeights[indexPath] ?? 0.0))
}
else {
return UITableViewAutomaticDimension
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (lastContentOffsetY != nil){
tableView.setContentOffset(CGPoint(x: 0, y: lastContentOffsetY!), animated: false)
}
}
func loadOlderOwnWorldMessages(){
profileFunctions.loadOlderOwnWorldMessages(startIndex: (userWorldMessages.count)) { response, newUserWorldMessages in
if let response = response {
if response.type == 1 {
DispatchQueue.main.async(execute: {() -> Void in
if newUserWorldMessages != nil {
var insertRows : [IndexPath] = []
var fromIndex = UserWorldMessagesStore.shared.worldMessages.count - 1
for worldMessage in newUserWorldMessages! {
UserWorldMessagesStore.shared.worldMessages.append(worldMessage)
insertRows.append(IndexPath(row: fromIndex, section: 1))
fromIndex += 1
}
self.userWorldMessages = UserWorldMessagesStore.shared.worldMessages
self.tableView.beginUpdates()
self.tableView.insertRows(at: insertRows, with: .automatic)
self.tableView.endUpdates()
}
})
} else {
DispatchQueue.main.async(execute: {() -> Void in
self.handleResponses.displayError(title: response.title, message: response.message)
})
}
}
}
}
@objc func bubbleTappedHandler(sender: UITapGestureRecognizer, should: Bool) {
let touchPoint = sender.location(in: self.tableView)
if let indexPath = tableView.indexPathForRow(at: touchPoint) {
if indexPath == currentSelectedWorldMessageIndexPath {
// it was already selected, so deselect it
deselectCurrentSelectedWorldMessage()
} else {
// select new one, deselect old one if was selected
if (currentSelectedWorldMessageIndexPath != nil){
deselectCurrentSelectedWorldMessage()
}
selectWorldMessage(indexPath: indexPath)
}
}
}
func selectWorldMessage(indexPath: IndexPath) {
tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none)
currentSelectedWorldMessageIndexPath = indexPath
if let cell = tableView.cellForRow(at: indexPath) as? ProfileWorldMessageCell {
// Change some constraints
cell.messageLabelTopConstraint.constant = 14
cell.messageLabelBottomConstraint.constant = 14
UIView.animate(withDuration: 0.15, animations: {
self.view.layoutIfNeeded()
self.tableView.beginUpdates()
self.tableView.endUpdates()
}, completion: nil)
}
}
@objc func deselectCurrentSelectedWorldMessage(){
UIMenuController.shared.setMenuVisible(false, animated: true)
if (currentSelectedWorldMessageIndexPath == nil){
return
}
let indexPath = currentSelectedWorldMessageIndexPath!
if let cell = tableView.cellForRow(at: indexPath) as? ProfileWorldMessageCell {
// Change back some constraints
cell.messageLabelTopConstraint.constant = 10
cell.messageLabelBottomConstraint.constant = 10
UIView.animate(withDuration: 0.15, animations: {
self.view.layoutIfNeeded()
self.tableView.beginUpdates()
self.tableView.endUpdates()
}, completion: nil)
}
currentSelectedWorldMessageIndexPath = nil
}
@objc func bubbleLongPressHandler(sender: UILongPressGestureRecognizer, should: Bool) {
// Show options like copy, delete etc.
}
}
推荐答案
您的cellForRow
函数有些错误非常:
- 这些单元已被重用,而您忽略了这一事实.每次调用
cellForRow
时,您都将创建手势识别器的新实例,并且当单元被重用时,您将添加另一个实例.这意味着,如果您的单元已被重复使用了50次,它将添加50个手势识别器.您只能创建一次手势识别器 - 您每次创建一个
DateFormatter
并设置其dateFormat
属性,这实际上是非常昂贵的过程.相反,您应该为每种日期格式设置一个静态的formatter实例
- the cells are reused, and you are ignoring that fact. You are creating new instances of gesture recognizers each time
cellForRow
is called, and when cell is reused you are adding yet another one. That means that if your cell had been reused for 50th time, it will have 50 gesture recognizers added. You should create your gesture recognizers only once - You create a
DateFormatter
and set itsdateFormat
property each time, and that is actually really expensive process. Instead you should have a static instance of formatter for each date format
这篇关于TableView的数据不完整的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!