【问题标题】:Is it better to hide a tableviewcell or filter in data source? (performance issue)在数据源中隐藏表格视图单元或过滤器更好吗? (性能问题)
【发布时间】:2019-07-20 08:36:12
【问题描述】:

我有一个 UITableViewController,其中有要隐藏的单元格。

我目前正在做的是隐藏 heightForRowAt 返回 0 的单元格和 cellForRowAt 返回 isHidden = false 的单元格。但由于我使用的是这个解决方案,我注意到当我在 tableView 中滚动时应用程序变慢了。

// Currently returning a height of 0 for hidden cells
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    if let post = timeline?.postObjects?[indexPath.row], post.hidden ?? false {
        return 0.0
    }
    return UITableView.automaticDimension
}

// And a cell with cell.isHidden = false (corresponding to identifier "hiddenCell")
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if let post = timeline?.postObjects?[indexPath.row] {
        if post.hidden ?? false {
            return tableView.dequeueReusableCell(withIdentifier: "hiddenCell", for: indexPath)
        } else {
            return (tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell).with(post: post, timelineController: self, darkMode: isDarkMode())
        }
    }
}

我在想为什么不对数组应用过滤器以完全删除tableView的隐藏单元格,但我不知道每次过滤它们是否对性能很好......

// Returning only the number of visible cells
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return timeline?.postObjects?.filter{!($0.hidden ?? false)}.count
}

// And creating cells for only visible rows
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if let post = timeline?.postObjects?.filter{!($0.hidden ?? false)}[indexPath.row] {
        return (tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell).with(post: post, timelineController: self, darkMode: isDarkMode())
    }
}

什么是最好的选择?生成单元格时隐藏单元格(第一个)还是将它们排除在列表中(第二个)?

【问题讨论】:

  • 尝试这两种方法。测试性能并确定哪个更适合您。

标签: ios swift uitableview


【解决方案1】:

我建议让表格视图数据源方法来处理timeline过滤 版本。但是,不要在cellForRowAt 方法中这样做,因为我们需要这样做一次,而不是每次绘制单元格。

因此,您可以做的是声明filteredTimeline 并在viewDidLoad 方法中执行一次过滤(例如):

class TableViewController: UIViewController {
    // ...
    var filteredTimeline // as the same type of `timeline`


    override func viewDidLoad() {
        // ...

        filteredTimeline = timeline?.postObjects?.filter{!($0.hidden ?? false)}

        // ...
    }

    // Returning only the number of visible cells
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return filteredTimeline.count ?? 0
    }

    // And creating cells for only visible rows
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if let post = filteredTimeline?.postObjects?.filter{!($0.hidden ?? false)}[indexPath.row] {
            return (tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell).with(post: post, timelineController: self, darkMode: isDarkMode())
        }
    }

    // ...
}

如果filteredTimeline = timeline?.postObjects?.filter{!($0.hidden ?? false)}viewDidLoad 有更好的位置,您可能需要致电tableView.reloadData()

你可以做的另一种选择:

如果您认为不需要原始的timeline,您可以自行过滤:

timeline = timeline?.postObjects?.filter{!($0.hidden ?? false)}
tableView.reloadData()

而且您不需要额外的过滤数组。


额外提示:

如果在heightForRowAt 方法中为某一行返回0.0 值,cellForRowAt 甚至不会被调用;例如:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 2
}

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return indexPath.row == 0 ?? 0.0 : 100.0
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    // ...
}

此时,cellForRowAt 应该只被调用一次,因为第一行的高度是 0.0

【讨论】:

  • 如果我创建一个过滤数组,内存中的对象保存了两次(在时间线和过滤时间线中),或者过滤时间线包含对时间线的引用(所以一个对象和更少的内存使用)?
  • @NathanFallet 好吧,它们将是两个不同的副本,如果使用filter(更多内存),则写入时复制将不适用,但是我们应该考虑调用额外的if-statements每次以绘制单元格为目标时...
  • @NathanFallet 我更新了答案(“你可以做的另一种选择”,如果它对你有用的话)。此外,虽然我假设创建一个额外的过滤数组不会占用内存,但您可以检查:stackoverflow.com/questions/1898161/memory-vs-performance
【解决方案2】:

单元格大小为 0 是没有意义的。最好的办法是过滤数据源,但我的建议是同时保留两个数组。

但是在其他地方处理过滤然后在 numberOfRowsInSection 中。


var filteredObjects = []

func filterObjects() {
    filteredObjects = timeline?.postObjects?.filter{!($0.hidden ?? false)}
}


// Returning only the number of visible cells
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return filteredObjects.count
}

// And creating cells for only visible rows
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    if let post = filteredObjects[indexPath.row] {
        return (tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell).with(post: post, timelineController: self, darkMode: isDarkMode())
    }
}

我不知道您是如何处理过滤器的,但只要您想应用过滤器,您只需

filterObjects()
tableView.reloadData()

【讨论】:

    猜你喜欢
    • 2016-11-15
    • 2017-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-17
    • 1970-01-01
    相关资源
    最近更新 更多