I have a table view showing search results. It has an array of all objects to search through, and an array of filtered objects, which feeds the table view.
When the user changes the text in the search bar, I update the filtered array and reload the table view. In addition, each cell has a thumbnail which is either cached or downloaded asynchronously before updating the table view cell.
So, the two places where the table view's data is reloaded are in the filtering method, which calls tableView.reloadData(), and in the cellForRowAtIndexPath, which calls self.tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimationStyle: .None) on the main thread, if the image needed to be downloaded.
So. I've been having all kinds of problems. The three kinds of errors I've gotten:
Fatal error: index out of range. This seems to be happening when cellForRowAtIndexPath is trying to get the contents from the array after it's been emptied because the user changed the search text.
Errors with the table view: e.g., error trying to insert row 34 into section 0. I'm not sure what's causing this to happen, but I guess it's trying to insert rows when the table view has no sections due to the array being empty.
Internal Inconsistency exception: I don't get any extra information when this happens, it just crashes. So I don't know what the problem is here either.
So how can I avoid these problems? I've been trying for a couple days now to get rid of these errors but nothing I try works. Does anyone have any ideas? Thanks.
Thanks to someone on Reddit, I found the answer.
Before updating the thumbnail image after asynchronously downloading it, I needed to check if the cell is still being used for the same index path (or for any index path.) Also, I switched from asking the table view to reload the row at that index path to setting the image of the cell's image view.
Basically, inside of cellForRowAtIndexPath, after determining the image needed to be downloaded and then doing so asynchronously, I switched from this:
NSOperationQueue.mainQueue().addOperationWithBlock {
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .None)
}
to this:
NSOperationQueue.mainQueue().addOperationWithBlock {
if indexPath == tableView.indexPathForCell(cell) {
cell.imageView?.image = downloadedThumbnail
}
}
Since I made this change I haven't gotten any crashes.
Related
Is it possible to use below library in ios when the tableview data is getting from server. Initially the row count will be zero until we get the data from server.
https://github.com/malkouz/ListPlaceholder
https://github.com/samhann/Loader.swift
Please help me if its possible using this.
I need to show FB like placeholder in tableview until I get data from server.
It certainly looks like it does what you want - although I'm not sure how customisable it is? Grab the 'Example' project and have a play!
However, I'd suggest taking a different approach and just return a row count of 1 while your loading your data. Then in your cellForRowAt: method create a 'loading cell' if your still loading which you can style to look exactly how you want. When your data finishes loading just call reloadData on your tableview, this time returning the correct row count and returning your fully populated cells.
I have multiple data in my UITableView and when I delete one record.
Don't use didEndDisplayingCell to manipulate the cell or the data source array by the passed index path.
It's just a notification that the specified cell was removed from the table
The documentation also states:
Use this method to detect when a cell is removed from a table view, as opposed to monitoring the view itself to see when it appears or disappears.
The solution is to move the entire code in this method to the location where the cell / data source item is removed and use it before removing anything
The app is crashing because even though you have deleted the object( so now array will have one less element) but you have not told the table view to reset itself according to new datasource. Even cellforRowAt will crash if you delete the last element. One solution is to reload the tableview by calling:
<tableViewName>.reloadData()
but it will produce unnecessary flicker if tableViews are quite large and complex. And the Second thing, You should'nt be using didEndDisplayingCell
This cool expanding tableView works great but when you select some of the rows, a weird animation occurs.
The table has multiple sections, and each row can show additional rows when clicked. Row appearance and behavior is structured in a plist using various values e.g. if a row's boolean "isExpandable" property in the plist is set to true and its "additionalRows" property is set to 2, the row can expand to show 2 sub-rows.
The issue with the table is that when the first cell in each section is clicked, the animation runs fine, but when other cells are clicked, they shuffle:-
I suspect the strange animation happens because all sections are reloaded when a cell is clicked because of this line under didSelectRowAtIndexPath in the ViewController file:-
tbl.reloadSections(NSIndexSet(index: indexPath.section), withRowAnimation: .Automatic)
I've tried the following but they either crash the app or cause the cells to not expand:-
Setting the animation method to .None
Using reloadData instead
Storing the indexPaths of the selectedRow and its sub-rows, and then reloading all with reloadIndexPaths
Storing the rows of the selected cell and its subcells then reloading them once when a row is clicked
Someone on the actual site that the code is posted on suggested using insertRowsAtIndexPath and deleteRowsAtIndexPath instead of reloadSection, but I'm not sure how to do it. Your help is greatly appreciated.
EDIT
- Also tried insertRowsAtIndexPath and deleteRowsAtIndexPath but it seems to conflict with the way the tableView model is set up and the app crashes.
Inserting rows into the table should help you with more consistent table view animation behaviour.
To insert rows at specific index paths you can use the 'insertRowsAtIndexPaths' method. You will need to generate each index path that you are inserting and then pass them to the method in an array.
ObjC:
NSArray* arr = your NSIndex Paths...;
[tableView insertRowsAtIndexPaths:arr withRowAnimation:UITableViewRowAnimationFade];
Swift:
table.insertRowsAtIndexPaths([IndexPaths..], withRowAnimation: .Automatic)
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/#//apple_ref/occ/instm/UITableView/insertRowsAtIndexPaths:withRowAnimation:
Here is the problem: reloadSections(..) makes the tableView conveniently fetch all of the newest data from the model (for the specified section(s)) at the cost of more 'vague' animation. Use insertRowsAtIndexPaths(..) with the .Top animation style for the behavior you are looking for. If you have issues with your Data Model, fix that first (because it will almost certainly bother you later).
I used xcode's premade Master detail application and built off of that. For the tableview, I am making a json request and storing that into coredata; and trying to populate the table based on that coredata.
The way it works, is that the user logs in and then a request is made for a user's specific team. The resulting title and url are saved into coredata and used to populate the tableview/detail view.
Although the coredata is being updated (printing out the logs), the tableview itself is never being updated. I have tried putting self.tableView.reloadData() in viewDidLoad, viewDidAppear, and viewWillAppear; I have also tried creating a timer that calls the reloadData every .4 seconds (current code) and I never see the table cell's textLabel updated. However, if I tap on a cell, the link that is loaded in the detail view IS updated. So to re-iterate, the coredata is being updated, but the table never updates its label text.
If I stop running the app through xcode and then run it again, then the tableview is populated with the labels; but changing the team/user leads to the same results (the coredata/links are updated and the text labels are not)
My code is definitely not the cleanest/best, just trying to get it to work at the moment.
Here's my MasterViewController.swift: https://gist.github.com/aryanaziz/a9791a82061f156afdb9
EDIT: Found the issue.
I was using the self.tableView.beginUpdates() function but was never using the self.tableView.endUpdates() which was causing problems. Removing the beginUpdates() had the tableview behaving like normal.
Found the issue. I was using the self.tableView.beginUpdates() function but was never using the self.tableView.endUpdates() which was causing problems. Removing the beginUpdates() had the tableview behaving like normal.
I have several cells that are written onto a UICollectionView and reloading them takes a long time. How can I reload only when the data of a cell is modified? Here's one thing I've tried:
if([oldDataSource count] != [currentDataSource count])
{
[collectionView reloadData];
}
Yet, I still don't want to deal with reloading the entire thing. I've also tried:
if([oldDataSource count] < [currentdataSource count])
{
//something was added. //reload the last item in the current data source
}
This also failed because I can't reload something that doesn't "exist" in memory yet. This method also doesn't work because if something was deleted from my data source, the Cell will still remain in the interface, and will crash if clicked on.
So, what's the best way to deal with edited cells?
Thanks!
Reloads just the items at the specified index paths.
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths
Discussion
Call this method to selectively reload only the specified items. This causes the collection view to discard any cells associated with those items and redisplay them.
try and get the cell u want to change. then:-
If u can get it. change the data source and reload it.
if it doesnt reflect the changes. change the data source, then the repective new values of the cell, then call setNeedsDisplay on the cell.
If the cell is not returned (that means) it is out of the view. Simply change the data source. The change in the data source will be reflected when the cell is brought to view
try any of these three.
cheers. have fun