我正在开发一个与“Instagram”中的用户界面相同的应用。我有一个饲料墙,其中用户滚动并查看朋友添加的新图片,类似于instagram。每个UITableViewcell都有一个图片和它自己的部分Header视图。我通过为每个细胞制作切片来实现这一目标。但我担心记忆泄漏
这是我的代码
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 45))
headerView.tag = 101
let imageView :UIImageView = UIImageView(frame: UtilityManager.setFrameWithOutHeight(CGRectMake(22, 5 , 30, 30)))
imageView.backgroundColor = UIColor.blueColor()
imageView.tag = 101
imageView.layer.cornerRadius = 15.0
imageView.clipsToBounds = true
imageView.layer.borderWidth = 1
imageView.layer.borderColor = UIColor.grayColor().CGColor
headerView.addSubview(imageView)
var label = UILabel(frame: UtilityManager.setFrameWithOutHeight(CGRectMake(50, 5, 320 - 60 , 35)))
label.textAlignment = NSTextAlignment.Left
label.backgroundColor = UIColor.clearColor()
label.textColor = UIColor(red: 1, green: 0.28, blue: 0.27, alpha: 1)
label.text = "I'am a test label"
label.font = UIFont(name: Constants.FONT_MEDIUM, size: 12)
headerView.addSubview(imageView)
headerView.backgroundColor = UIColor.clearColor()
return headerView
}
顺便说一下,我正在使用Swift。它真酷,不是吗;)。让我解释一下这个委托函数的更多信息来制作自定义部分的标题。每当我的任何单元格或部分出现在屏幕上时,即使我滚动回已经分配了headerView的部分,也会调用此委托方法。因此在滚动时为一个部分创建多次headerView。但我知道这是错误的方法必须有一些东西来检查天气headerView已经创建或不。如果我的视图确实存在于内存中,那么有没有办法检查代码,那么就不需要再次分配内存了。 有人请帮助:)。我甚至会感激一点帮助。
答案 0 :(得分:4)
您通常需要Cocoa为表格单元格提供的相同内存管理和单元格重用模式,因此请使用UITableViewHeaderFooterView
。
在viewDidLoad:
致电tableView.registerClass(UITableViewHeaderFooterView.classForCoder(), forHeaderFooterReuseIdentifier:"Header")
现在,您可以在委托方法中按需出列这些视图,例如:
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView: UITableViewHeaderFooterView
if let reusedView = tableView.dequeueReusableHeaderFooterViewWithIdentifier("Header") as? UITableViewHeaderFooterView {
// Remove the existing contents of the reused view
map(reusedView.contentView.subviews) {
$0.removeFromSuperview()
}
headerView = reusedView
} else {
headerView = UITableViewHeaderFooterView(reuseIdentifier:"Header")
}
let imageView = ... // Set up image view
let label = ... // Set up label
headerView.contentView.addSubview(imageView)
headerView.contentView.addSubview(label)
return headerView
}
Cocoa现在根据需要处理和重用这些标题视图。
如果您继承UITableViewHeaderFooterView
并添加图像和标签属性(然后您不必删除然后将子视图添加到出列视图中,只需按名称替换它们),此实现就会更直截了当。此外,如果您为班级设计笔尖或使用表格视图单元格通过故事板进行查看,则队列将永远不会返回为零。
既然你提到你可能有“无限单元”,也许你担心在内存中保留太多的数据源(也许你有大量的大图像用于那些图像视图)。在这种情况下,您需要设置某种缓存。如果你到达那一点,看看NSCache
。无需实现自己的缓存。
根据OP的要求,我将详细说明这个出队系统如何在Cocoa中运行。您的tableView:cellForRowAtIndexPath:
委托方法中可能有类似的代码。想象一下,您的用户向下滚动查看表格视图。您的表格视图顶部的单元格和标题会在屏幕外显示,而表格视图底部会显示新的单元格和标题。所有这些单元格都具有相似的格式。现在假设Cocoa没有做任何特殊的事情(没有细胞队列)。消失的细胞将被解除分配,并且将从头开始创建出现的细胞。相反,Cocoa试图提高效率并保留这些单元/标题的队列。它不会创建任何超过它在任何时候显示在屏幕上的任何东西。当其中一个单元格离开屏幕时,它会将其放入一个队列中,然后该单元格可供您稍后出列(从队列中取出)。当您将单元格或标题出列(根据其重用标识符字符串请求它)时,它将为您提供屏幕外(或其中一个)的确切视图。获得后,您可以根据需要进行配置。是的,在上面的代码中,您仍然必须创建UIImageView
和UILabel
(我将在下面解释您如何能够并且应该做得更好),但主UIView
和任何类型装饰不需要改变的视图仍然存在(重用它们没有计算成本)。因此,Cocoa基本上可以帮助您回收这些视图,使事情变得非常有效和安全。
你会注意到,当上面的代码试图将一个单元格出列时,它会在if let
语句中测试它是否为零。这很重要,因为如果您只是加载表视图,它在队列中没有任何单元格/视图给您,所以如果它是nil,那么您从头开始实例化一个新视图。实例化它时,为其提供重用标识符字符串。如果此视图稍后在屏幕外显示,Cocoa知道将其放入特殊队列以便稍后再回复给您。如上所述,但是,如果您的视图是从笔尖或故事板加载的,则队列将始终返回非零值。关于从笔尖加载的最后一个细节,您需要使用registerNib:forHeaderFooterReuseIdentifer:
的{{1}}方法注册重用标识符。
现在你可以看到Cocoa如何非常高效地处理这些单元格/视图,不需要你在用户滚动表格视图时实例化新视图,你可能会对我上面的代码感到有些失望,但仍然会删除所有旧的子视图和实例化新的UITableView
和UIImageView
,确实你应该感到失望。推荐的解决方法是通过继承UILabel
,或者你可以真正子类化任何UITableViewHeaderFooterView
。为imageView和label创建一个属性。现在,不要使用旧内容销毁子视图,然后再次创建这些视图,只需使用新的适当内容(UIView
和imageView.image = newUIImage
)替换其内容。信不信由你,这将节省大量的计算。 label.text = newString
的创建成本有些高。子类化的一种草率(尽管可能更灵活)替代方法是使用UIView
属性将标记放在要插入的视图上,然后使用tag
获取对这些视图的引用。
而且我讨厌所有关于标题的讨论都是无用的,但我会做出最后的观察。我注意到你说每个单元格都有自己的标题。如果这是真的,那么只需制作一个大的表格单元格(将标题内容放在正常的表格视图单元格内容之上),你可能会更好。您可以一起跳过使用标题。我不知道你的应用程序中发生了什么,所以我只是让你对那个做出判断。
祝你好运@Ankush!知道headerView.viewWithTag(anInt)
周围的绳索是一项很棒的iOS技能,我相信你将来会发现它很有用!
答案 1 :(得分:1)
您需要自己实现标头视图的缓存。框架不会为你做这件事。您可以创建一个数组实例变量来保存所有已分配的视图。然后在委托方法中创建并缓存或只返回标题视图。
var headerViews = [UIView]()
func createHeaderView() -> UIView {
let headerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 45))
...
return headerView
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
while section >= headerViews.count {
headerViews.append(createHeaderView())
}
var headerView = headerViews[section]
// Set up the header view (image, text, ...)
return headerView
}
请注意,这可能不是最有效的方法,因为一旦创建它们,您将保留所有标题视图。当您知道同时显示的最大标题视图数时,您可以合并某种缓存逐出策略并重用现有的标题视图。
以下是上述代码的变体,最多可重用20个标题视图。只要彼此分开20个部分的标题不会同时显示在屏幕上,这就可以正常工作。
var headerViews = [UIView]()
func createHeaderView() -> UIView {
let headerView = UIView(frame: CGRectMake(0, 0, tableView.frame.size.width, 45))
...
return headerView
}
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let maxNumHeaderViews = 20
let index = section % maxNumHeaderViews
while index >= headerViews.count {
headerViews.append(createHeaderView())
}
var headerView = headerViews[index]
// Set up the header view (image, text, ...)
return headerView
}
然而,关键点可能是您在问题中发布的代码中没有内存泄漏。只要显示,Cocoa就会保留您保留的视图,并在不再需要时将其释放。