我在改进UITableView的滚动方面遇到了麻烦,该UITableView的自定义单元格使用autolayout进行布局。我有两种不同高度的自定义UITableViewCell(一个大约200px一个大约500px)我尝试了很多东西,包括:
cellForRowAt
移至willDisplayCell
以及许多声称可提高滚动速度的在线指南。但是,滚动仍然是生涩的!
此外,当我尝试滚动到顶部单元格时,它有时无法到达顶部。我猜估计的细胞高度与此有关,但由于细胞高度不同,很难做到正确。
仪器建议当我将文本设置到我的单元格的标签(即调整单元格高度的位)时,我的代码会变慢。
我试图弄清楚Twitter应用程序(具有与我的UITableView基本相同的功能,具有属性文本和图像等)如何具有如此好的滚动功能。我从他们那里看到了一篇博文,说明了如何使用drawRect,但是,我很确定他们现在不会这样做。任何人都对如何实现60fps平滑滚动有任何见解?
首先,我的两个表格单元格具有以下结构:
表格单元格1:
- UITableViewCell
- UIView (content view)
- UIView (Shadow view)
- UIView (Card background)
- UIView x 3
- UIButton x 5
- UILabel x 2
- UITextView
我拥有名为shadow view
和card background
的UIViews的原因是因为我向shadowPath
添加了shadow view
的{{1}}层。然后我向UIBezierPath
添加一个半径。这两个不能在同一层上完成,因为半径仅在两个角中,并且在尝试同时具有阴影和半径时会出现问题。
表格单元格2:
card background
这使用相同的代码添加阴影和圆角。
要获得行的估计高度,请使用以下代码:
- UITableViewCell
- UIView (content view)
- UIView (Shadow view)
- UIView (Card background)
- UIView x 3
- UIButton x 5
- UILabel x 3
- UITextView
- UIImageView x 2
在我的UITableViewController中的func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
let type = model.dataContent[indexPath.row]["type"].stringValue
if type == MediaType.text.rawValue {
return 150
} else {
return 500
}
}
内,我有以下代码:
cellForRowAt
当我设置两个UITableViewCells的indexPath时,会在自定义单元格类中调用此代码来更新单元格:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Create the custom cell
let mediaTableCellIdentifier = "MediaCell"
let textTableCellIdentifier = "TextCell"
let type = MediaType(rawValue: model.dataContent[indexPath.row]["type"].stringValue)
var cell: TableCell!
if type == .text {
cell = tableView.dequeueReusableCell(withIdentifier: textTableCellIdentifier, for: indexPath) as! TableTextCell
if flowState == .individualPost {
(cell as! TableTextCell).expandView()
}
} else {
cell = tableView.dequeueReusableCell(withIdentifier: mediaTableCellIdentifier, for: indexPath) as! TableMediaCell
(cell as! TableMediaCell).postImageView.image = nil
}
cell.delegate = self
if type == .image {
let cell = cell as! TableMediaCell
let urlString = model.dataContent[indexPath.row]["img"]["loc"].stringValue
// Check if we have an image stored in our cache for the image URL. If not, download it.
if let cellImage = model.imageCache[urlString] {
cell.postImageView.image = cellImage
}
else {
model.downloadImage(
atIndexPath: indexPath,
type: type!,
progress: { percentage in
cell.progressView.isHidden = false
cell.progressView.updateCirclePath(percentage: percentage)
},
completion: { [weak self] error, image in
if let strongSelf = self {
if error == nil {
// Store the image in to our cache
strongSelf.model.imageCache[urlString] = image
// Update the cell with our image
if let cellToUpdate = tableView.cellForRow(at: indexPath) as? TableMediaCell {
cellToUpdate.postImageView.image = image
cellToUpdate.progressView.resetCircleAnimation()
cellToUpdate.progressView.isHidden = true
}
}
else {
print("Error downloading image: \(error!.localizedDescription)")
}
}
}
)
}
} else if type == .video {
let cell = cell as! TableMediaCell
let urlString = "\(CloudFrontURL.video.rawValue)/\(model.dataContent[indexPath.row]["video"]["loc"].stringValue)"
if let cellVideo = model.videoCache[urlString] {
cell.videoPlayer = AVPlayer(playerItem: AVPlayerItem(asset: cellVideo))
cell.videoPlayer!.actionAtItemEnd = AVPlayerActionAtItemEnd.none
cell.videoView.playerLayer.player = cell.videoPlayer
} else {
let videoURL = URL(string: urlString)
cell.videoPlayer = AVPlayer(url: videoURL!)
model.videoCache[urlString] = cell.videoPlayer?.currentItem?.asset
cell.videoPlayer!.actionAtItemEnd = AVPlayerActionAtItemEnd.none
cell.videoView.playerLayer.player = cell.videoPlayer
}
}
cell.caption.text = model.dataContent[indexPath.row]["caption"].stringValue
cell.caption.isHidden = true
model.retrieveCaption(at: indexPath, forCell: cell, withFont: cell.caption.font!) { (caption, cellToUpdate) in
if let theCell = cellToUpdate {
theCell.caption.attributedText = caption
theCell.caption.isHidden = false
}
}
cell.commentCount.text = model.dataContent[indexPath.row]["commentsCount"].stringValue
// Enable/Disable the controls based on our settings
cell.controlsEnabled = controlsEnabled
// Provide each cell with our data
cell.model = model
// Setting this makes the controls update its data automatically.
cell.indexPath = indexPath
// If we've reached the last post in our loaded data, get the next page of posts (unless it's an individual post).
if (indexPath.row == model.dataContent.count - 3 && !lastItemReached && flowState != .individualPost) {
stateMachine.enterState(.retrievingNextPage)
}
return cell
}
答案 0 :(得分:0)
UITableView
显示速度缓慢的一个非常明显的原因是您的cellForRowAtIndexPath
太长了。您需要重构代码。保持Cell声明本身简短。
最佳做法是在cellForRowAtIndexPath
声明Cell并调用一个用内容填充Cell的bindData
函数。例如:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! Cell
let content = contentArray[indexPath.row]
cell.bindData(content)
return cell
}
}
extension Cell {
bindData(content: ContentStruct) {
label.text = content.name
}
}
同样在您的情况下,您应该在设置内容的函数和设置属性的函数之间进行重构。保持每个功能更短。
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("cell") as! Cell
let content = contentArray[indexPath.row]
cell.bindData(content)
cell.setProperties
return cell
}
}
extension Cell {
bindData(content: ContentStruct) {
label.text = content.name
}
setProperties() {
// set stuff like:
// Enable/Disable the controls based on our settings
cell.controlsEnabled = controlsEnabled
// Provide each cell with our data
cell.model = model
// Setting this makes the controls update its data automatically.
cell.indexPath = indexPath
// If we've reached the last post in our loaded data, get the next page of posts (unless it's an individual post).
if (indexPath.row == model.dataContent.count - 3 && !lastItemReached && flowState != .individualPost) {
stateMachine.enterState(.retrievingNextPage)
}
}
}