swift:从本地 json 文件填充的 uitableview 在滚动时严重卡顿
Posted
技术标签:
【中文标题】swift:从本地 json 文件填充的 uitableview 在滚动时严重卡顿【英文标题】:swift: uitableview populated from a local json file stutters heavily while scrolling 【发布时间】:2018-01-18 02:13:24 【问题描述】:当按钮被点击以继续到 tableview 时,它需要大约 5 秒才能继续。在它最终 segues 之后,当 tableview 滚动时,它会结结巴巴,有时会崩溃。表格视图由本地 json 文件填充并引用本地图像。图像被优化为小尺寸。是什么导致了这种情况,我该如何优化/修复我的代码以阻止这种情况发生?
import UIKit
class PDList: UIViewController, UITableViewDelegate, UITableViewDataSource
@IBOutlet weak var pdTableView: UITableView!
var pdArt = [Decode]()
override func viewDidLoad()
super.viewDidLoad()
json()
pdTableView.delegate = self
pdTableView.dataSource = self
func json()
let path = Bundle.main.path(forResource: "palmdesert", ofType: "json")
let url = URL(fileURLWithPath: path!)
do
let data = try Data(contentsOf: url)
self.pdArt = try JSONDecoder().decode([Decode].self, from: data)
catch
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return pdArt.count
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "pdCell")
let image = UIImage(named: pdArt[indexPath.row].pic)
cell.textLabel?.text = pdArt[indexPath.row].art
cell.detailTextLabel?.text = pdArt[indexPath.row].artist.capitalized
cell.imageView?.image = image
return cell
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
return 100
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
performSegue(withIdentifier: "pdDetail", sender: self)
self.pdTableView.deselectRow(at: indexPath, animated: true)
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
if let destination = segue.destination as? PDDetail
destination.pdArt = pdArt[(pdTableView.indexPathForSelectedRow?.row)!]
@IBAction func done(sender: AnyObject)
dismiss(animated: true, completion: nil)
JSON 示例:
"art": "Agave",
"desc": "Agave by Michael Watling serves as the City's entry monument and is inspired by the imagery of the agave, a succulent native to the desert. The stone forms are representative of Palm Desert's many well-managed resources for survival and growth.",
"artist": "Michael Watling",
"lat": 33.7215,
"long": -116.362,
"pic": "test.png"
时间简介:
如果您需要任何其他信息,请告诉我。任何帮助表示赞赏。
【问题讨论】:
为什么每次都创建一个新单元而不是使单元出队? 请仅完全展开主线程(选项点击)并过滤掉系统调用(从调用树按钮弹出) @WarrenBurton 我在上面的帖子中添加了展开的图片。 那么,什么是 IndioList,为什么分配一个命名图像需要 295 毫秒? .这会导致严重的口吃。 还将时间选择过滤到您感兴趣的部分。 【参考方案1】:我构建了一个示例 UITableView
项目,其中包含 20 张 4032 × 3024
的大型 jpeg 图像的资产库,以通过 UIImage(named:)
输入我的单元格。
对于120 x 70
的图像视图,这大大超出了规格,因此最大要求为360 x 210
。
时间分析器显示这一点是为了在全新的一批 12 个单元格中滚动。
每个单元大约 54 毫秒 (650/12)。然而,即使是大型 JPG 也能利用硬件解码,所以也不会太不好。
60Hz 刷新的现代 iWidget 为您提供每帧最多 16 毫秒的单元格刷新时间。所以我结巴了。
用适当缩放的图像 (360 x 210) 替换图像即可。
每个单元大约 14 毫秒。表格滚动大部分平滑。
所以这里的问题很可能是其中之一。
您的图片太大。确保您的资产的像素大小为3x
图像视图的大小。
您的图像不是 PNG 或 JPEG,不能交给硬件解码器或用pngcrush
处理。确保您的图像是 PNG 或 JPEG。例如 GIF 会很糟糕。
当 JPG 可能更合适时,您正在使用 PNG。真实世界的图像最好是 JPG。线条艺术/纯色最好是 PNG。
【讨论】:
【参考方案2】:出列单元格,而不是每次都创建新单元格。将 cellForRow 更改为以下内容:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell = tableView.dequeueReusableCellWithIdentifier("pdCell", forIndexPath: indexPath)
let image = UIImage(named: pdArt[indexPath.row].pic)
cell.textLabel?.text = pdArt[indexPath.row].art
cell.detailTextLabel?.text = pdArt[indexPath.row].artist.capitalized
cell.imageView?.image = image
return cell
【讨论】:
我用您建议的答案替换了我的 cellForRow,但当我滚动时它继续卡顿。 有没有试过把设置图片的代码注释掉看看效果? 我注释掉了图像和细节文本行,但它仍然只有 textLabel 口吃。 使用时间分析工具查看延迟在哪里。 @Paulw11 我在上面的帖子中添加了时间分析器图片。【参考方案3】:我的想法是创建一个类似这样的类或结构。
struct ObjectFromJSON
var art = ""
var desc = ""
var artist = ""
var lat = 0.0
var long = 0.0
var pic = ""
解码数据的地方创建一个数组:
var decodedData = [ObjectFromJSON]()
然后将所有解码的数据添加到这个数组中。如果这没有帮助,那么尝试添加一个名为 SwiftyJSON 的 CocoaPod,它会使 JSON 解码变得轻而易举,并且可能会有所帮助。
解码后只需将信息设置到 ObjectFromJSON 对象中并将其附加到 decodedData 数组。
let decodedArt = json["art"].stringValue
let decodedDesc = json["desc"].stringValue
let decodedArtist = json["artist"].stringValue
let decodedLat = json["lat"].doubleValue
let decodedLong = json["long"].doubleValue
let decodedPic = json["pic"].stringValue
let newObject = ObjectFromJSON(art: decodedArt, desc: decodedDesc, artist: decodedArtist, lat: decodedLat, long: decodedLong, pic: decodedPic)
decodedData.append(newObject)
这是基于您的 JSON,但是如果您在该 json 中有超过 1 个对象,则将它们放入数组中然后解码看起来会有些不同,例如,如果您为对象编号而不是类似的东西:
let decodedArt = json[0]["art"].stringValue
如果您有多个 JSON 文件,那么第一个解决方案应该没问题
【讨论】:
如何将 ObjectFromJSON 结构值替换为 Decode 结构中的值?我实现了 SwiftyJSON,它产生的结果与我在帖子中使用上述方法解析时产生的结果相同。 基本上你正在解码的只是一个字符串,所以在解码时你正在创建一个新对象并填充它的详细信息: 检查我的答案编辑希望它能澄清一点。还要注意,如果您有一个存储多个艺术家的 JSON 文件,那么问题在于 JSON,您必须将其重构为一组艺术家。如果您对此有任何疑问,请告诉我。但是,如果您有多个 JSON 文件并且内容是您粘贴的内容,那么您应该可以使用我添加的代码。以上是关于swift:从本地 json 文件填充的 uitableview 在滚动时严重卡顿的主要内容,如果未能解决你的问题,请参考以下文章
从 Swift 中的 Json 响应中填充 TableSection
从 Firebase 下载 json 并填充 TableView Swift 3