ScrollToItemAtIndexPath for Infinite Collection View - ios

I have an infinite collection view, and every time it appears, I'd like to display a specific indexPath. I attempted putting the scrollToItemAtIndexPath method in viewDidAppear, viewWillAppear, and viewDidLayoutSubviews to no avail.
On viewDidLayoutSubviews, the view shows up properly but displays the wrong index. It ends up being offset by 24 indexes. When I print the index, I see the proper index in the console. When I set the index in cellForItemAtIndexPath, it also displays the proper index, but all of the cells are just that index.
Here is the code I have in viewDidLayoutSubviews:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
infiniteCollectionView.layoutIfNeeded()
self.infiniteCollectionView.scrollToItemAtIndexPath(self.initialIndexPath!, atScrollPosition: .CenteredHorizontally, animated: false)
}
This displays the cell 24 indexes before the one I actually want to be presented. I assume that I'm scrolling the collection view before all the cells are being loaded properly, therefore causing it to display the incorrect index.
How would I go about resolving this issue?

Related

UICollectionView cell is disappearing when dragging out of initial bounds (Swift3, CollectionView, Drag and Drop)

I have a problem, when dragging cell out of initial bounds.
On drag, I take value from UILongPressGestureRecognizer location and set cell "center" property to this location.
The problem occurs when I scroll down, below initial bounds, the cell doesn't show on the view (although there are cells existing down, probably loaded after first view appearing). When I scroll little up, within same take (press), cell shows up again.
I tried several things
cashing cells, so view alway get the same cell instance on reload
override func viewDidLayoutSubviews which lead in endless loop
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myCollection.collectionViewLayout.invalidateLayout()
}
collectionView?.clipsToBounds = false
None of these worked for me. Could you help me? Thanks!!!

Alternatives to tableView.scrollToRow()

Problem
I need to have a UITableView reload with a specific cell positioned at the top of the tableview. For instance, have the 3rd cell in the tableView appear at the top of the tableView, rather than the 1st.
The issue in doing this comes from not being able to use the
self.tableView.scrollToRow(at: indexPath, at: .top, animated: false)
function, because I have a custom header that expands and collapse when the user scrolls. That means when I call the scrollToRow() function, my header collapses, messing things up.
Question
Is there any way to have a tableView open/reload with the 3rd cell positioned at the top of the tableView without using a "scroll" function?
Thanks!
You can follow below steps to fulfill your requirement:
Identify NSIndexPath of one of the visible cells.
Get its rectForRowAtIndexPath.
Get the current contentOffset of the table, itself.
Or, you can use scrollRectToVisible
This is how we use contentOffset:
mainTableView.setContentOffset(rectForRowAtIndexPath.point, animated: true)
And scrollRectToVisible:
mainTableView.scrollRectToVisible(rectForRowAtIndexPath.point, animated: true)

What is the expected `contentOffset` of UITableView when calling reloadData()?

I have a UITableView that displays custom cells. By pressing a button, the user can change the content of my UITableView
Scenario:
Load an array with 1000 elements
Scroll table to bottom (gives me about 20,000 contentOffset.y after scrolling)
On button pressed, I replace my array with 300 elements
Call reloadData in main thread (gcd)
After the step 4, what should be the correct new immediate contentOffset.y of my UITableView? I expected it to be 0 and my table scroll to top but for some reason it's not and does not scroll back to top. Is this the normal behavior of UITableView?
From Apple's docs... https://developer.apple.com/reference/uikit/uitableview/1614862-reloaddata
For efficiency, the table view redisplays only those rows that are
visible. It adjusts offsets if the table shrinks as a result of the
reload.
So, yes, it's normal to not scroll all the way to the top on reloadData.
If you want it to scroll to the top, either use setContentOffset or:
.scrollToRow(at: IndexPath(row: 0, section: 0), at: UITableViewScrollPosition.top, animated: false)
Yes, It is normal behavior of UITableView. If you an want that table view should scrolled to top after reloading, you have to do that using below code.
let newOffset = CGPoint(x: currentOffset.x, y: 0)
tableView.setContentOffset(newOffset, animated: false)
Automatically, UITableView retain it's scroll offset while reloadData function called.

Previous Cells remain after using collectionView.reloadData?

I have all of my view stylings in the method
override func viewWillAppear(_ animated: Bool) {
displayDataForIndexPath()
}
Which is called when I need to reload data in my ViewController. My ViewController has a CollectionView that I need to refresh.
I've added collectionview.reloadData() to my displayDataForIndexPath() method. However, all the previous cells remain, instead of disappearing, so the new cells stack on top of the old ones. What is the proper method to remove those old cells so the CollectionView reloads properly?
you dont need to delete your datasource
in your cellForItemAtIndexPath you should not add more controls to the cell. it should be only done one time on creation of the cell.
as the name states when you normaly get your cell: reuse and that means it reuses from the last state it displayed in the collection.

Prevent scrolling when appending to or updating UITableView using NSFetchedResultsController with autolayout

I have a chat application which backs a single-section UITableViewController with a core data Messages table. Based on push messages, I update the status of a message or append new messages. When a new message arrives, I'd like to scroll to the bottom of the table. Each cell has variable height, which is driven by autolayout, using:
self.tableView.estimatedRowHeight = 76
self.tableView.rowHeight = UITableViewAutomaticDimension
I load the NSFetchRequest() in viewDidLoad, which loads the messages from a core data table, and jumps to the last message in viewDidAppear. This works properly when running from Xcode, but if I start the app on an iPhone instead, it doesn't jump to the last message, but somewhere in the middle. Also, if I defer the message to the main thread, it still doesn't work. However, if I insert even a small delay like this, it always works properly:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// not sure why this delay is needed
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.001 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
self.scrollToLastMessage()
}
}
Of course I don't like having a fixed delay, especially one I don't understand. Is there a specific event that I should hook for scrolling to the last message?
More vexing, I can't find a way to have my NSFetchResultsController .Insert (append) or .Update a row without the UITableViewController scrolling to another location. I've tried capturing and restoring the contentOffset, which I have to do animated: false to prevent very screwed up animations, but the table still has visible scrolling flicker. I've tried reloadData() for the whole table, reloadRowsAtIndexPaths() to just update the relevant cell, with and without beginUpdates() / endUpdates(). I also tried scrollToRowAtIndexPath(lastRow). No matter what, I can't get this simple update to work without the UITableView autonomously scrolling. It seems to scroll the changed row to the middle of the screen with animation, after which I have to scroll it back to where it should be. Has anyone else noticed this? Is this scrolling an artifact of NSFetchResultsController, a "feature" of UITableViewController or a result of using UITableViewAutomaticDimension?
So far I get the best results with the following, but there is visible flicker.
func controllerDidChangeContent(controller: NSFetchedResultsController!) {
self.tableView.reloadData()
}
private func scrollToLastMessage() {
dispatch_async(dispatch_get_main_queue()) {
// without the check below, this oddly doesn't scroll to the right place (autolayout issue?)
if self.tableView.contentSize.height > self.tableView.frame.size.height {
let offset = CGPoint(x: 0, y: self.tableView.contentSize.height - self.tableView.frame.size.height)
self.tableView.setContentOffset(offset, animated: false)
}
}
}
I notice when using UITableViewAutomaticDimension that the offset of the last row changes. For instance, if I ask for the offset at one time, in order to jump to the right contentOffset to display it, the contentOffset can spontaneously change as UITableViewController probes other cells for their heights and scrolls around. Is there a way to prevent this? I.e., to get UITableViewAutomaticDimension to always measure the real height of cells rather than the estimated height, so that I don't have to implement a rowHeight method? As mentioned above, I've also tried scrollToRowAtIndexPath(lastRow), but that has the same weird, scrolling behaviour.

Resources