How to detect when a UITableViewCell becomes hidden or removed from memory when it has an edit control with focus

When a cell is scrolled out of view and is removed, uikit calls didenddisplayingcell. However, if that same cell has a uitextfield and the uitextfield is the first responder, didenddisplayingcell does not get called. If the user then taps a button to save the contents of all the cells and did not scroll the cell into the view, cellforrowatindexpath returns nil for that cell. How is it possible to identify when the cell is going to be hidden and removed from memory in this scenario?

Answered by litehouse in 59101022

Thank you for your suggestions. I do appreciate it. I discovered the kind of solution that I was looking for. I noticed in the UIScrollView class the property keyboardDismissMode. One of the values is UIScrollViewKeyboardDismissModeOnDrag. In interace builder, I set the property to dismiss the keyboard on drag in the tableview. This causes the UITextView to give up first responder and consequently didEndDisplayingCell is called. This is the perfect solution because when the cell was hiddin without this setting, characters could still be entered into the text field.


BTW, this problem exists for tableviews with static or dynamic cells. Cells scrolled off the screen are removed from the display in static mode.

I am not sure about how to detect when a cell is going to be hidden. But you may be able to avoid this problem if you don't use your view (cells) to store your model data. This kind of issue is precisely why the model and the view should be separate entities in the MVC paradigm. You should update your model data immediately when the user enters something in the cell. Then when the user hits the save button, you save what's in your model. It doesn't matter whether the cell is on screen or not.

That's the need for detection to make sure you have the data to store it in the model. Otherwise, you are recording every key tap and storing it somewhere outside the view. Tapping save is the point at which you transfer it from the view to the model. The view doesn't store the model data. The view is the vehicle for accepting the data from the user so it can be stored in the model. There's no other way to take data from the user and place it in the model. It must go through the view first and there must be a way to grab it from the view at the most propitious times. This is an edge case that i don't have a solution for.

Fair enough. Does your text field delegate get a textFieldDidEndEditing message? Does the cell get a prepareForReuse? Maybe you could use one of those.

Yes exactly. Save the text on EVERY KEYSTROKE. You can save it in something you call the temporary model, but that's how it's done.


BTW, this isn't the only problem. What happens if the textfield has text in it and scrolls offscreen and then back onscreen? If you haven't saved it somewhere the text will be lost.

The framework provides an elegant solution to handle this in every scenario EXCEPT the one described. All the scenarios including the one i describe can be handled cleanly and with small amounts of code in the didEndDisplayingCell method in the UITableViewDelagate. This method even handles the case you describe if the UITextField is not the first responder. HOWEVER, the reason it does not work in the scenario described is because it's the ONE and ONLY case where they do not call didEndDisplayingCell, even though it is no longer being displayed, and worse the cell is removed from the TableView.


In addition, when the cell is removed from the UITableView, it does not call method removeFromSuperview in the UITableViewCell when the UITextField is the first responder in the cell. I know that it is not in the TableView because if i call cellForRowAtIndexPath, it returns nil. For all other cells that scroll out of the view, this method is called as well as the method didEndDisplayingCell in the UITableViewDelegate.


Now if these 2 methods are not called, I would expect to get a non-nil value when cellForRowAtIndexPath is called. There are other reasons besides the scenario I describe for why the application would want to detect when cell is scrolled off screen and removed from the tableview. Seemingly they recognize this and provided methods for it except for this one case.

I hear you. I've been assessing other approaches. It's a lot of overhead to handle a common edge condition - especially since they provide a method in the UITableViewDelegate and the UIView that was designed for detecting this condition, except this is the ONLY case where they are not called even though the conditions for calling them appear to be true to me, and I can test that the condition is true in the code.

You seem to have done a thorough analysis. So what's the solution?

I don't have one yet. I only know what doesn't work and the interactions with the delegates. That's why I'm here to learn if others have solved this in an elegant way.

Another possibility is to use static table view cells. Then you'd get the semantics you're expecting since the textfields won't be reused or released.

Accepted Answer

Thank you for your suggestions. I do appreciate it. I discovered the kind of solution that I was looking for. I noticed in the UIScrollView class the property keyboardDismissMode. One of the values is UIScrollViewKeyboardDismissModeOnDrag. In interace builder, I set the property to dismiss the keyboard on drag in the tableview. This causes the UITextView to give up first responder and consequently didEndDisplayingCell is called. This is the perfect solution because when the cell was hiddin without this setting, characters could still be entered into the text field.


BTW, this problem exists for tableviews with static or dynamic cells. Cells scrolled off the screen are removed from the display in static mode.

A cell with a text field that is first responder and is scrolled off-screen is still considered being displayed - which is why didEndDisplayingCell is not called. This allows it to keep its text and still receive keyboard input. When the text field resigns first responder then didEndDisplayingCell is called.

I was able to KVO UITableViewCell.layer.hidden as an indication that the cell has been removed from the table view

How to detect when a UITableViewCell becomes hidden or removed from memory when it has an edit control with focus
 
 
Q