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

循环一个 json 文件并填充一个数组 Swift

显示本地存储的图像以响应 JSON 表填充结果

如何在 Swift 2 中从 JSON 填充我的 tableView?

如何让 json 在 Swift 3 中填充 UITableView?