Swift: 同时重置多个子类自定义uitableviewcells?
Swift: Reseting multiple subclassed custom uitableviewcells at the same time?
我有 3 个自定义单元格,它们是从 viewController Uitable 视图中子class 编辑的。这些单元格有手势识别器来查看它们在哪里触摸以更改图标/背景,然后将结果保存到核心数据。
我想要这样的情况,当任何单元格没有被触摸 3 秒(删除保存按钮)时,数据被保存并且单元格重置为基色和图形。我已经全部完成,但我无法使用任何方法同时重置它们。我尝试了很多不同的方法,我认为最好的方法是在 viewcontroller 中重新加载 table,但我无法让它工作。
目前我正在使用一个带有布尔值的结构。当触摸在任何单元格上结束时,它会调用一个计时器,如果它在没有另一个触摸的情况下结束,它会调用一个方法来保存和重置。
也许这是错误的方法,所以我有点卡住了。
查看控制器class
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) ->
Int {
return measurements.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("BmCustomCell", forIndexPath: indexPath) as! BmTableViewCell
return cell
} else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCellWithIdentifier("StressCustomCell", forIndexPath: indexPath)as! StressTableViewCell
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("PainCustomCell", forIndexPath: indexPath) as! PainTableViewCell
return cell
}
}
painTableViewCell 示例class(与其他两个相同)
// if touched the text changes and the icon changes. if not touched for 5 seconds resets to not touched.
func resetTouchedPainAfterFiveSecs (){
if TouchNotification.isBeingTouched == false {
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: Selector("reset"), userInfo: nil, repeats: false)
} else {
timer.invalidate()
}
}
func reset(){
ResetTables().resetAllTables()
savePainData()
}
// reset pain ui to ready to go state
func resetPain () {
TouchNotification.reset = true
if TouchNotification.reset == true {
painLabel.textColor = whiteColour
painIcon.image = UIImage(named: "IconPain 0 Unused")
touchedPain = false
painLabel.text = "Pain"
TouchNotification.reset = false
}
}
触摸通知
struct TouchNotification {
static var isBeingTouched = false
static var reset = false
}
重置class
class ResetTables: UIViewController {
func resetAllTables(){
StressTableViewCell().resetStress()
BmTableViewCell().resetBM()
PainTableViewCell().resetPain()
}
}
看来问题是您正在创建对象的新实例,而不是使用屏幕上的实例。
举个例子,
ResetTables().resetAllTables()
调用
func resetAllTables(){
StressTableViewCell().resetStress()
BmTableViewCell().resetBM()
PainTableViewCell().resetPain()
}
您对 ResetTables()
所做的是创建 class ResetTables
的新对象,然后对其调用 resetAllTables()
,这又会创建一个新单元格 StressTableViewCell
,并对其调用 resetStress
。其他两个单元格也是如此。尽管您正在 调用这些方法,但您并不是在显示的单元格上调用它们,而是在新的单元格上调用它们,在函数离开作用域后,这些单元格将被释放。
所以在您的 reset()
方法中,您想要重置单元格本身,而不是创建您重置的新单元格。
你说"I thought the best way would be to reload the table in the view controller but I couldn't make it work"。是的,这正是我建议你做的(尽管我可能会建议 reloadRowsAtIndexPaths
)。
一个更微妙的问题是,尤其是当您的 UI 变得越来越复杂时,单元格可以滚动到屏幕之外,可以重复使用等等。因此,您通常希望避免直接更新单元格。如果这些单元格中的一个或多个在需要重置时已经滚出屏幕怎么办?当它滚动回视图时,您如何知道如何处理该单元格?等等
因此,您经常希望将 UI(单元格)与模型(关于哪些单元格是什么颜色的信息)分离。一旦你这样做了,你就可以移动到一个你有一些模型结构的世界(在我下面的例子中,cellBackgroundColors
),它跟踪一个单元格应该是什么颜色。然后 cellForRowAtIndexPath
将根据此模型对象设置单元格的颜色。然后,每当您想更改单元格的颜色时,您 (a) 更新此模型对象,然后 (b) 告诉 table 视图重新加载该单元格。
如果您想同时更新多个单元格,则可以更新您的模型并使用单元格数组调用 reloadRowsAtIndexPaths
,然后它们将同时更新。但是您也将以安全的方式执行此操作,这不依赖于当时屏幕上的单元格。而且,如果我正确地推断您希望计时器在他们点击其他内容时重新启动,您可以 invalidate
计时器并在用户每次点击单元格时创建一个新计时器。
这是一个小例子:
class ViewController: UITableViewController {
var cellBackgroundColors: [UIColor]!
let defaultColor = UIColor.whiteColor()
override func viewDidLoad() {
super.viewDidLoad()
resetCellBackgroundColorsToDefault()
}
func resetCellBackgroundColorsToDefault() {
cellBackgroundColors = [UIColor.redColor(), UIColor.yellowColor(), UIColor.cyanColor()]
}
// MARK: UITableViewDataSource
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
// do whatever cell configuration you want here
// but programmatically set the background color (or whatever) based upon the model:
if indexPath.row < 3 {
cell.backgroundColor = cellBackgroundColors[indexPath.row]
} else {
cell.backgroundColor = defaultColor
}
return cell
}
// MARK: UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
cellBackgroundColors[indexPath.row] = UIColor.blueColor()
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
timer?.invalidate()
timer = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: "resetCells:", userInfo: nil, repeats: false)
}
// MARK: Timer code
weak var timer: NSTimer?
func resetCells(timer: NSTimer) {
resetCellBackgroundColorsToDefault()
let indexPaths = [0, 1, 2].map { NSIndexPath(forRow: [=10=], inSection: 0) }
tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Fade)
}
}
我有 3 个自定义单元格,它们是从 viewController Uitable 视图中子class 编辑的。这些单元格有手势识别器来查看它们在哪里触摸以更改图标/背景,然后将结果保存到核心数据。 我想要这样的情况,当任何单元格没有被触摸 3 秒(删除保存按钮)时,数据被保存并且单元格重置为基色和图形。我已经全部完成,但我无法使用任何方法同时重置它们。我尝试了很多不同的方法,我认为最好的方法是在 viewcontroller 中重新加载 table,但我无法让它工作。 目前我正在使用一个带有布尔值的结构。当触摸在任何单元格上结束时,它会调用一个计时器,如果它在没有另一个触摸的情况下结束,它会调用一个方法来保存和重置。 也许这是错误的方法,所以我有点卡住了。
查看控制器class
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) ->
Int {
return measurements.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCellWithIdentifier("BmCustomCell", forIndexPath: indexPath) as! BmTableViewCell
return cell
} else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCellWithIdentifier("StressCustomCell", forIndexPath: indexPath)as! StressTableViewCell
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("PainCustomCell", forIndexPath: indexPath) as! PainTableViewCell
return cell
}
}
painTableViewCell 示例class(与其他两个相同)
// if touched the text changes and the icon changes. if not touched for 5 seconds resets to not touched.
func resetTouchedPainAfterFiveSecs (){
if TouchNotification.isBeingTouched == false {
timer = NSTimer.scheduledTimerWithTimeInterval(2.0, target: self, selector: Selector("reset"), userInfo: nil, repeats: false)
} else {
timer.invalidate()
}
}
func reset(){
ResetTables().resetAllTables()
savePainData()
}
// reset pain ui to ready to go state
func resetPain () {
TouchNotification.reset = true
if TouchNotification.reset == true {
painLabel.textColor = whiteColour
painIcon.image = UIImage(named: "IconPain 0 Unused")
touchedPain = false
painLabel.text = "Pain"
TouchNotification.reset = false
}
}
触摸通知
struct TouchNotification {
static var isBeingTouched = false
static var reset = false
}
重置class
class ResetTables: UIViewController {
func resetAllTables(){
StressTableViewCell().resetStress()
BmTableViewCell().resetBM()
PainTableViewCell().resetPain()
}
}
看来问题是您正在创建对象的新实例,而不是使用屏幕上的实例。
举个例子,
ResetTables().resetAllTables()
调用
func resetAllTables(){
StressTableViewCell().resetStress()
BmTableViewCell().resetBM()
PainTableViewCell().resetPain()
}
您对 ResetTables()
所做的是创建 class ResetTables
的新对象,然后对其调用 resetAllTables()
,这又会创建一个新单元格 StressTableViewCell
,并对其调用 resetStress
。其他两个单元格也是如此。尽管您正在 调用这些方法,但您并不是在显示的单元格上调用它们,而是在新的单元格上调用它们,在函数离开作用域后,这些单元格将被释放。
所以在您的 reset()
方法中,您想要重置单元格本身,而不是创建您重置的新单元格。
你说"I thought the best way would be to reload the table in the view controller but I couldn't make it work"。是的,这正是我建议你做的(尽管我可能会建议 reloadRowsAtIndexPaths
)。
一个更微妙的问题是,尤其是当您的 UI 变得越来越复杂时,单元格可以滚动到屏幕之外,可以重复使用等等。因此,您通常希望避免直接更新单元格。如果这些单元格中的一个或多个在需要重置时已经滚出屏幕怎么办?当它滚动回视图时,您如何知道如何处理该单元格?等等
因此,您经常希望将 UI(单元格)与模型(关于哪些单元格是什么颜色的信息)分离。一旦你这样做了,你就可以移动到一个你有一些模型结构的世界(在我下面的例子中,cellBackgroundColors
),它跟踪一个单元格应该是什么颜色。然后 cellForRowAtIndexPath
将根据此模型对象设置单元格的颜色。然后,每当您想更改单元格的颜色时,您 (a) 更新此模型对象,然后 (b) 告诉 table 视图重新加载该单元格。
如果您想同时更新多个单元格,则可以更新您的模型并使用单元格数组调用 reloadRowsAtIndexPaths
,然后它们将同时更新。但是您也将以安全的方式执行此操作,这不依赖于当时屏幕上的单元格。而且,如果我正确地推断您希望计时器在他们点击其他内容时重新启动,您可以 invalidate
计时器并在用户每次点击单元格时创建一个新计时器。
这是一个小例子:
class ViewController: UITableViewController {
var cellBackgroundColors: [UIColor]!
let defaultColor = UIColor.whiteColor()
override func viewDidLoad() {
super.viewDidLoad()
resetCellBackgroundColorsToDefault()
}
func resetCellBackgroundColorsToDefault() {
cellBackgroundColors = [UIColor.redColor(), UIColor.yellowColor(), UIColor.cyanColor()]
}
// MARK: UITableViewDataSource
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 100
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
// do whatever cell configuration you want here
// but programmatically set the background color (or whatever) based upon the model:
if indexPath.row < 3 {
cell.backgroundColor = cellBackgroundColors[indexPath.row]
} else {
cell.backgroundColor = defaultColor
}
return cell
}
// MARK: UITableViewDelegate
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
cellBackgroundColors[indexPath.row] = UIColor.blueColor()
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
timer?.invalidate()
timer = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: "resetCells:", userInfo: nil, repeats: false)
}
// MARK: Timer code
weak var timer: NSTimer?
func resetCells(timer: NSTimer) {
resetCellBackgroundColorsToDefault()
let indexPaths = [0, 1, 2].map { NSIndexPath(forRow: [=10=], inSection: 0) }
tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Fade)
}
}