Collection view loaded in reverse order - swift

I have a collection view which is loaded based on the API(Get method).The number of entries increases when new entry is added and it is reloaded with new entry at the end.
How to reload the collection view with last showing first, second last as second and so on.

If you want to display array in reverse then you can do it like this:-
array.reversed()
OR
if you want to display last added item on top, then insert item at first index:-
yourArray.append(item)
let indexPath = IndexPath(row: 0, section: 0)
collectionView.insertItems(at: [indexPath])

Related

Swift Diffable Snapshot - Add new Section to Snapshot and Reload Collection View

I have a collection view that has sections and the beginning. I want to add a new section after the user creates data and display data in the new section. Here is the code I have tried but failed. The new section does not appear after running the code.
var snapshot = self.collectionView.diffableDataSource.snapshot()
let recentlySection = Section(title: "Recently Colored",
cells: [userPicture],
sectionType: .recentlyColored)
snapshot.appendSections([recentlySection])
snapshot.appendItems([userPicture], toSection: recentlySection)
self.collectionView.diffableDataSource.apply(snapshot, animatingDifferences: true)
self.collectionView.reloadData()
Thank you!
I have solved my problem. I am leaving answer here in case of anyone encounters the same problem. I forgot to add new section to my collection view sections list.
Adding this line solved my problem.
self.collectionView.sections.insert(recentlySection, at: 2)

Getting an 'NSInternalInconsistencyException', reason: 'Invalid section 0.' when doing a search on multiple sections

I am getting an exception when doing a search on multiple section. It occurs when applying a snapshot on the datasource.
Background: I have (pre-defined) sections, and each section has a collection of items. Sections won't appear in the viewController if there are no items in section. Items are added by a function of the app. Once an item is added in one of the section, datasource update is called and will show the section with the item added.
Problem: Encountering this issue when trying to search for a non-existent item twice. To reproduce, you can enter a non-existent item, then delete the search string via a backspace, then input a non-existing item again, then error will be thrown on the dataSource.apply().
Hoping someone can help. TIA!
Here is the code:
func updateData(on searchItem: String = "") {
//create a snapshot that will be used by the datasource to apply the diff changes
snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
Manager.shared.getAllSections().forEach { section in
let items = section.items
//if search string is empty, we just assign the items of the section,
//else we filter it based on the searchItem
var filteredItems = searchItem.isEmpty ? items :
items.filter { $0.itemName.lowercased().contains(searchItem.lowercased()) }
//if theres no items filtered, we ain't appending any section and items
if filteredItems.count > 0 {
snapshot.appendSections([section])
snapshot.appendItems(filteredItems)
}
}
//when calling apply, i get the exception when calling apply on dataSource
dataSource.apply(snapshot, animatingDifferences: false)
}
//Here is the updateSearchResults delegate method triggered when typing something in the search bar
func updateSearchResults(for searchController: UISearchController) {
guard let searchedItem = searchController.searchBar.text, !searchedItem.isEmpty else {
updateData()
return
}
updateData(on: searchedItem)
}
ok so I think this is some type of internal bug in diffable data source where it doesn't like when you have 0 sections in your collection view, but the workaround I figured out was to just add a dummy section and hide the section header (if you have one).
in your updateData() method you could add:
if snapshot.numberOfItems == 0 {
snapshot.appendSections([YourSection(name: "dummy")])
}
Then if you're using a section header, give that dummy section some identifiable variable that you can use to hide the header.
When dequeuing a supplementary view (header), check if the name == "dummy" then hide the header if so.
It's a hack, but it ends up looking the exact same and you don't have to deal with ugly empty sections being displayed.
My understanding is that that happens when the compositional layout is trying to resolve layout for a section that doesn't exist in the data source.
In my case I was using UICollectionViewCompositionalLayout(sectionProvider:) which was returning old sections when my data source was returning the correct ones but different.
How I fixed it was invalidating the layout: collectionView.collectionViewLayout.invalidateLayout()

How to get the index of a cell given the text inside in XCUITest?

I'm testing a tableview the cell content in XCUItest. In my case, I don't know the order of the cell text, nor am I allowed to set an accessibility id for the text. How can I get the index of a cell given the text inside?
For instance, if I wanted to get the index of the cell containing text "Cell 2 Text" I would try something like this:
func testSample() {
let app = XCUIApplication()
let table = app.tables
let cells = table.cells
let indexOfCell2Text = cells.containing(.staticText, identifier: "Cell 2 Text").element.index(ofAccessibilityElement: "I dunno")
print(indexOfCell2Text)
}
I feel like I'm close, but I'm unsure. Can anyone suggest a solution?
I apologize if this question has been asked before. I wasn't able to find anything specific about this.
References I visited beforehand:
https://developer.apple.com/documentation/xctest/xcuielementquery/1500842-element
How can I verify existence of text inside a table view row given its index in an XCTest UI Test?
iOS UI Testing tap on first index of the table
The most reliable way really is to add the index into the accessibility identifier. But, you can't. Can you change the accessibility identifier of the cell instead of the text ?
Anyway, if you don't scroll your table view, you can handle it like that :
let idx = 0
for cell in table.cells.allElementsBoundByIndex {
if cell.staticTexts["Text you are looking for"].exists {
return idx
}
idx = idx + 1
}
Otherwise, the index you will use is related to cells which are displayed on the screen. So, after scrolling, the new first visible cell would become the cell at index 0 and would screw up your search.
for index in 0..<table.cells.count {
if table.cells.element(boundBy: index).staticTexts["Your Text"].exists {
return index
}
}

Move UITableView row to new position programatically Swift 3

I'm looking to move an item in a tableview to a new position. I have models acting as my data source.
var models = [Model]
var dataTableView = UITableView!
A model class looks like this
class Model {
timestamp: Int64
...
}
The models data source is sorted by timestamp, any item inserted should be in it's right position.
When an item's timestamp changes, I want to move it to the corrected sorted position.
What's the best way to do this?
I'm considering just sorting the array to get the item to it's correct position then find it's index and do the following
beginUpdates()
reloadRows(at: [INSERTED_ITEM_INDEX, OLD_INDEX], with: .bottom)
endUpdates()
So that by reloading both index paths, both cells will display correctly.
Another option would be to find which index the item will occupy if inserted into the list (without actually touching the list) and then do the following:
beginUpdates()
moveRow(at: ORIGINAL_INDEX, to: NEW_INDEX)
reloadRows(at: [ORIGINAL_INDEX, NEW_INDEX], with: .bottom)
endUpdates()
Which is the better approach?
None? would be happy to learn new approaches, thanks :)

Swipe Cell in UICollectionView - Swift

Is there an elegant and short way to progrematiclly swipe between two cells (assuming we have the desired NSIndexPath of the two cells)?
I see few possibilities here, having the information you provide.
1) You can use standard UICollectionView method: - moveItemAtIndexPath:toIndexPath:, but you must update your data source first. For example, assume you already updated data source (note that this example code is useless until you figure out index changes after moving items.):
collectionView.performBatchUpdates({ () -> Void in
collectionView.moveItemAtIndexPath(firstIndexPath,toIndexPath:secondIndexPath)
//note: proper indexes might be changed after moveItem.. method. Play with it and you'll find the proper one for your item.
collectionView.moveItemAtIndexPath(secondIndexPath,toIndexPath:firstIndexPath)
}, completion: { (finish) -> Void in
})
2) You can recalculate your layout if you use custom layout
3) You can just reload collection view with reloadData or reloadItemsAtIndexPaths E.g.:
var dataSourceArray = [1,2,3,4,5]
// some event occurred -> dataSourceArray changed
dataSourceArray = [1,2,5,4,3]
// call collectionView.reloadData() or reloadItemsAtIndexPaths(_:).
If you'll use 1st or 3rd way, in both cases data source must be up to date.