Crash on switching segment control - ios

I have a tableview with 4 segments on top. At the 1st segment I have loaded 76 rows, at segment 3 I have 4 rows, at 4th segment I have about 25 rows & there's nothing at segment 2. If the rows in each of the segment are not scrolled down and if all of them are at the top(i.e. the first row is visible out of the total number of rows for each segment) then no matter how I switch the tabs, there won't be any crash.
But say for instance if at the 1st segment, I scrolled the rows down and then shifted to the 3rd segment, then there's a crash saying fatal error: Index out of range. Maybe due to mismatch in the number of rows on either tabs. But not sure about the fix. Hope somebody can help...

Please check that below things are satisfied in your case OR not ?
While Changing the Segment Control , you have to reload the tableView
with NewDataSource, and so you are going to change the UI so please
put the code of reloadData in main thread.
Make sure numberOfRows are maintained through Delegate method while
changing the segment Control.
And last but not least , another way to prevent the crash is that ,
you can check the indexPath.row < yourArray.count in
cellForRowAtIndexPath Delegate Method. This will surelly prevent the crash occures for index out of range.

From this case i came to understand you are using same UITableView for three segments also i came to know your call tableView.reloadData() when you change segment the crash happen because of one segement have more data and other don't have when scroll tableView. tableView's visble index positions not available in other segement's datasource because of that only you get crash and solution for this you need to scroll to the 0th index if count greater than 0 then reload tableView
when click on other segement before changing value to be populated scroll back to 0th index
if listArray.count > 0{
let index = IndexPath(row: 0, section: 0)
self.tableView.scrollToRow(at: index, at: .bottom, animated: false)
}
then change the value for clickedSegement to listArray
then reload tableview
self.tableView.reloadData()
Hope this will help you

Related

How to get the top visible section header index in a UITableView of empty sections

I have a list with over 1000 sections that may be empty, meaning numberOfRows is 0 for every section.
Is there a way to get the index of the top most visible section?
Please do not tell me about indexPathsForVisibleRows, this only returns an IndexPath if numberOfRows is not 0. Hence, it does not work in this scenario. (Neither does indexPathsForRows(in:))
EDIT: The use case is a calendar view, that can be filtered. Usually, when scrolling up or down, the first visible section defines the header title. In the attached screenshot you see the problem.

Get first row in UITableView where unknown number of first sections are empty

I need the index path of the first row in a tableview under these conditions:
The first row could be off screen and so not in calls to methods that return an array of visible rows.
The first row's section is unknown, could be greater than 0, and the total number of sections is variable. This results from prior sections returning 0 to [UITableViewDataSource numberOfRowsInSection:] calls.
I could cycle through each section looking for rows but was wondering if UITableView has a better way to do this?
Based on the comments, it seems you want to know the index path of the row in the first section of the table view that has 1 or more rows.
Your only option is to iterate through your data source section by section until the section has 1 or more rows in it. Then the desired row will have an indexPath of row 0 and the section you just found.
There is no shortcut available through the table view to get this info.
Though there is one possible shortcut but it requires that your table view use "Plain" style (not "Grouped") and your table view can't have any header or footer views for any of the sections. If all of that is true, you can get the indexPath of the row you want by using the UITableView indexPathForRowAtPoint: and pass in CGPointMake(5, 5). That will give the index path of the topmost row in the whole table view.

Strange tableView row/section animation when row is selected

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).

UICollectionViewCell scrambled position when re-entering screen?

I have a UICollectionView which should show thumbnails of photos received from a server, much like the Photo app in iOS.
When the cells disappear from the view and then re-appear, their positions are reversed up until the point the finger releases the screen.
As soon as the finger releases the screen, the cells get properly re-positioned as well.
I can't figure out why iOS does this.
In the following GIF you'll see the cells, and a label inside indicating their index in their section.
As you can see, the index changes as I explained above.
Why is this happening?
Btw ignore the sixth image, I was just drinking soup.
EDIT: I noticed a pattern when the line of cells re-enter the screen.
This pattern is not visible in the image above, so I'll just have to explain it in text.
Ok, so at start as you can see the index of the images are increasing, from 0 to 4, right?
As I hold the finger down and scroll the line of cells in and out of view
the cell indexes change to 3, 1, 4, 2, 0.
followed by the second re-entering which changes their
indexes to 2, 1, 0, 4, 3.
On the third re-entering they change to 4, 1, 3, 0, 2.
And finally on the fourth re-entering they change back
to original indexes 0,1,2,3,4.
This happens as long as I scroll the first line of cells in and out of view.
This is super-weird and makes no sense at all to me.
EDIT 2:
Ok, so it seems that the re-ordering of the cells to their correct index occurs only when the their images are changed/re-applied.
I added a variable which indicates if they've already been cached which I call bool TNCached, and upon cellForItemAtIndexPath I call a method from within the cell which is called requestTN, which in turn downloads the thumbnail of that specific cell from the server.
When the app fully receives the thumbnail, it sets the variable TNCached to TRUE, so next time the cell re-enters the view it won't re-download the thumbnail, thus not updating the cell, which causes the weird re-ordering of the cells indexes to persist even after I release my finger from the screen.
Edit 3:
Yaaay fixed it!
Turns out that the re-use of my UICollectionView cells is the "problem".
Not really a problem, but whenever a cell is reused, the UICollectionView reuses random cells of any given cell of the ones that disappeared when scrolled outside the screen.
So my question now is if the efficiency of my re-use of cells is maybe not that efficient?
What's happening is that your cells are being re-used,
which means that any of the cells that have disappeared from the screen can be used again, in any order.
What you need to do at cellForItemAtIndexPath is to setup your cell again.
By "setup" I mean to take the information from the data-source and apply it to your cell again.
So if cell 0 re-appears at NSIndexPath section 0 and item 4, then you simply go to your datasource and read the data that should be presented at NSIndexPath section 0 and item 4.

Inserting the first cell into a collection view causes an assertion failure

If I try to insert an item into a UICollectionView when there are 0 sections and 0 items, I get an assertion failure.
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of sections. The number of sections contained in the collection
view after the update (1) must be equal to the number of sections
contained in the collection view before the update (0), plus or minus
the number of sections inserted or deleted (0 inserted, 0 deleted).'
A classic. The problem is, I'm modifying my data source and then inserting the item directly afterwards. The showFirstCell property is checked in all of the necessary data source methods and it isn't modified anywhere else.
self.showFirstCell = true
self.collectionView.insertItemsAtIndexPaths([NSIndexPath(forItem: 0, inSection: 0)])
Wrapping this in performBatchUpdates changes nothing.
I would like for this item to be added in an animated fashion. I don't think I should have to first check if there are items already in place, but doing so and calling reloadData instead does work, but it's not perfect.
Your number of sections don't match up.
Once you have decided to enter a section to the UICollectionView, you should return the same number in your numberOfSections function as well.
What's happening right now is that you insert a cell, and it calls your numberOfSections function again, where you are still returning 0, and it promptly crashes.
The reason why it didn't work and gave you an error is because your numberOfSections is set to 0 and it didn't update yet when you add the cell.
To fix that, just create (if you don't have already) an NSMutableArray, and for numberofSections, return that mutableArray. Then whenever you add an object to the collectionView, just add an object to the mutableArray of sections.
Next and most important part is: Anytime you change, add, or remove something from the collectionView, just do a beginUpdat and endUpdate.
If you want to insert item from your CollectionView, you need to
insert that item from your data source which is in your array. In this example I inserted the item to myArray
then I inserted it also to myCollectionView. It's working because
the total number of items from your array (data source) is equal to
the number of items in the collectionView.
myArray.insert(valuetoinsert, atIndex: 0)
let path:NSIndexPath = NSIndexPath.init(row: 0, section: 0)
myCollectionView.insertItems(at: [path as IndexPath])

Resources