I have a UITableView with a custom cell I built. The cell has several labels, a start button and a stop button, and an NSTimer that gets fired off and stopped from the buttons.
The labels display data from sqllite (through core data) and the timer runs individually for each separate cell.
This all works fine until I have more cells and cell reuse gets triggered. At that point, each cell shows the correct data in its labels, but if I start a timer for the first cell, the moment it rolls off screen, the next cell showing (and subsequently reusing the previous cell) will have its timer going as well.
I can't figure out how to guarantee each cell has its own timer thread with cell reuse.
Any help would be appreciated.
In your case the cells have internal state (timer). When cell is reused, it's state is lost (or even worth reused cell will inherit previous state). You have two options
don't reuse cells
make cells stateless (e.g. move your timers to controller)
Related
I have a basic UITableView that contains 20 cells, one of the cells is a MKMapView just dropped in as is without any custom code (not even setRegion:Animated), if I open the view at the first time and scroll down the table view (towards the map's cell), there is a noticeable hang happens for the app, and if I use setRegion:Animated (without dropping any pin) the hang gets longer. However, this block of the main thread disappears on later attempts to scroll towards the map's cell.
I can't use the MKSnapShotter because I want the user to interact with the map so an image won't satisfy the case.
the table view does not make any block on the main thread if the map does not exist.
how to avoid blocking the main thread while showing the map's cell for the first time ?
First make a tableView IB outlet if you haven't already done so. Create the cell in view did load using tableView.dequeueReusableCellWithIdentifier("mapCellIdentifier") and store that in an ivar. Then in the data source return the cell when the index path is the one you want so inside of cellForRowAtIndexPath do
if (indexPath.row == wantedRow) {
return mapCell
}
The mapCellIdentifier needs to be set in the cell you created in interface builder for the map. This is assuming you have dynamic cells.
I want to have a blinking image (swaps between two images) in table cells.
What seemed most natural to me is as follows:
Set a NSTimer to goes every .5 seconds, calling -blink:
In -blink: I toggle a blinkStatus variable on/off
In -cellForRowAtIndexPath:, I set the image depending upon the blinkStatus
Now, I also call [myTableView reloadData] in the -blink: method, so thinks get updated.
This seems to make sense; the state of the cells is actually changing (the image is being changed), so having the table update makes sense.
This works. However, the reloadData seems to interfere with the user interaction:
In table editing mode if the user selects (-) to delete, and the Delete button appears, it will disappear when reloadData occurs. It's easy enough only do the reloadData if [myTableView.editing] is false. (And it's not a big problem to not have blinking during editing mode.)
However, the reloadData still seems to mess things up (e.g. clear cell selection). I also worry about the efficiency of calling reloadData every .5 seconds.
Is there a smoother way to get that image blinking? I was thinking of saving a pointer to any blinking cells, and update the .image property in the timer. But this seems like a bad idea, since cells are reused for different rows of the table (ouch), and I doubt that simply updating the .image property would update the table without a reloadData.
You should just toggle the image in the -blink method, as long as you use the proper interfaces ( such as cell.imageView.image ) then the image will update automatically, no need to call reloadData
Did you try to use [UIImageView animationImages] property?
Just provide your images, set required duration and animationRepeatCount and call startAnimating
I have an app where the user can drag UITableViewCells from one TableView to another. I do this by rendering a "dummy" cell on top of the UITableViewCell that the user touches, and disable the "real" cell. I then insert a new row in the destination UITableView, after I have animated the dummy cell, to the position where the new cell will be inserted. This works as i want it to. But the other way around, causes me a lot of issues.
When a cell from the destination UITableView I double tapped, the cell should animate back to the source UITableView where it initially was dragged from. The problem is that I cannot remove the cell from the destination UITableView fast enough.
I want it to disappear instantly, so that when I render a dummy cell on top of it, I won't see an 2 different animations of the same cell. (the one where the dummy cell moves to the source UITableView, and the one where the actual cell is removed from the destination UITableView).
A reloadData on the UITableView is not good enough, because I still want the remaining cells to pull together on the now empty space, with an animation.
So, can I somehow create a custom animation, or set the duration of UITableViewRowAnimation to zero? UITableViewRowAnimationNone does not do anything for me.
Oh, and it might be important to mention that the app is not going on App Store, so I don't care if iI have to do something that Apple will not approve of.
We have a scenario where we reuse the same UITableView to display different sets of records from the database as a user selects an item in a different UITableView (similar in concept to a splitviewcontroller, but all in the same view).
When the user selects a different area to view, we fetch the appropriate records from the database, update the Source with the new set of records, and call ReloadData.
The problem is that some of the cells in the UITableView still exist and are reused when switching between lists, resulting in invalid or overlapped data for various rows.
The question I have is: is there a method whereby we can tell the UITableView to discard all existing cells including those queued for reuse? I would have expected ReloadData to perform this task, but it does not.
Update with additional clarification:
Each cell in our table is composed of a variable number of subviews (we are trying to mimic a grid control). When the user selects a different list in the left-hand navigation, there is no guarantee that the subview positions (columns) in the newly selected list will overlap with those in the previous list. This is why we are looking for a method to remove the queued cells.
Since this is a situation that others may find themselves in, I will post the solution that we used, but am still interested in more "native" ways of doing this.
Here is how we solved the issue:
When the set of records being displayed is changed, we update the UITableView with a unique identifier for the current cell configuration (i.e. which list of data is the user currently viewing).
We then created a custom version of UITableViewCell and record the unique list identifier in the cell when it is created.
When we dequeue a reusable cell, we compare the view's current unique identifier with the unique identifier stored in the cell and, if they don't match, we discard the cell and create a new one.
Hopefully this will help someone down the road.
ReloadData does not clear the re-use queue. It simply triggers the callbacks to re-read the data which reloads the rows. As each cell get's loaded, if you're getting it out the re-use queue it will still be used. That's good even if the table view is re-used because the cell types are the same type especially since you use the CellIdentifier to ensure that.
But, it's kind of odd that you're overlapping data. When you get a cell out of the re-use queue, how are you putting the data in it? Are you painting the data directly on the cell? Are you adding subViews with the data (causing multiple sets of overlapped data)? Typically, a cell contains subviews like text labels and the data is set on them so there's no scenario where data gets overlapped - the subviews of cell just has their data updated ...
Your GetCell method needs to ensure that a dequeued cell is fully configured with the new data that it is going to display.
A common mistake is assuming that just dequeuing the cell for display will give you a fresh cell that you can use.
If you have done any customization with your cell, like adding views, modifies its properties (images, disclosure indicators, background colors) you must make sure in the GetCell method that is reusing a cell that every single one of those properties is properly set before returning the cell.
I have a UITableView Controller and a UITableView. I have everything set up using the delegates etc, and it populates fine. I notice a small bug however with the following method:
:cellForRowAtIndexPath
I am noticing that this method is continually called every time I scroll the table. Even after the table is populated, it continues to call. Basically, a cell moves out of view, when it comes back in view, it is calling it again. I had NSLog print out the cell contents within that method, which is how I know it continues to call.
Should that function not just call once per cell, to populate it, then be done?
Nope. That's called every time a cell is needed, and a cell is needed every time one is rendered.
UITableView is a very clever little critter. It literally keeps only the cells it's displaying, plus a cache of literally two or three more. When the time comes to scroll a cell on that wasn't on, it can be asked for a cell from the cache, and if there is one, it gets reused.
This way HUGE data sets can be displayed in a UITableView, because there really aren't a large number of cells in memory, only the ones on the screen and a small cache.
This is correct behavior. It has to do with the dequeueing of UITableViewCells. That is why cell reuse is important. This way there are ideally a finite, small amount built in memory that continually get reused.
Check out the docs for more info and code samples.